diff --git a/backend/framework/domain/src/main/resources/migration/3.0.0/ddl/V3.0.0_5__api_test.sql b/backend/framework/domain/src/main/resources/migration/3.0.0/ddl/V3.0.0_5__api_test.sql index d553763f68..2d1280e2ed 100644 --- a/backend/framework/domain/src/main/resources/migration/3.0.0/ddl/V3.0.0_5__api_test.sql +++ b/backend/framework/domain/src/main/resources/migration/3.0.0/ddl/V3.0.0_5__api_test.sql @@ -8,8 +8,9 @@ CREATE TABLE api_debug( `protocol` VARCHAR(20) NOT NULL COMMENT '接口协议' , `method` VARCHAR(20) COMMENT 'http协议类型post/get/其它协议则是协议名(mqtt)' , `path` VARCHAR(500) COMMENT 'http协议路径/其它协议则为空' , + `pos` BIGINT NOT NULL DEFAULT 0 COMMENT '自定义排序' , `project_id` VARCHAR(50) NOT NULL COMMENT '项目fk' , - `module_id` VARCHAR(50) NOT NULL DEFAULT 'root' COMMENT '模块fk' , + `module_id` VARCHAR(50) NOT NULL DEFAULT 'root' COMMENT '模块fk' , `create_time` BIGINT NOT NULL COMMENT '创建时间' , `create_user` VARCHAR(50) NOT NULL COMMENT '创建人' , `update_time` BIGINT NOT NULL COMMENT '修改时间' , @@ -86,7 +87,9 @@ CREATE TABLE api_definition `delete_time` BIGINT COMMENT '删除时间', `deleted` BIT(1) NOT NULL DEFAULT 0 COMMENT '删除状态', PRIMARY KEY (id) -) COMMENT = '接口定义'; +) ENGINE = InnoDB + DEFAULT CHARSET = utf8mb4 + COLLATE = utf8mb4_general_ci COMMENT = '接口定义'; CREATE INDEX idx_project_id ON api_definition(project_id); diff --git a/backend/framework/plugin/plugin-api-sdk/src/main/java/io/metersphere/plugin/api/spi/AbstractApiPlugin.java b/backend/framework/plugin/plugin-api-sdk/src/main/java/io/metersphere/plugin/api/spi/AbstractApiPlugin.java index f2e6765022..500dd7133c 100644 --- a/backend/framework/plugin/plugin-api-sdk/src/main/java/io/metersphere/plugin/api/spi/AbstractApiPlugin.java +++ b/backend/framework/plugin/plugin-api-sdk/src/main/java/io/metersphere/plugin/api/spi/AbstractApiPlugin.java @@ -2,5 +2,8 @@ package io.metersphere.plugin.api.spi; import io.metersphere.plugin.sdk.spi.AbstractMsPlugin; +/** + * 接口插件抽象类 + */ public abstract class AbstractApiPlugin extends AbstractMsPlugin { } diff --git a/backend/framework/plugin/plugin-api-sdk/src/main/java/io/metersphere/plugin/api/spi/AbstractProtocolPlugin.java b/backend/framework/plugin/plugin-api-sdk/src/main/java/io/metersphere/plugin/api/spi/AbstractProtocolPlugin.java new file mode 100644 index 0000000000..793e98a529 --- /dev/null +++ b/backend/framework/plugin/plugin-api-sdk/src/main/java/io/metersphere/plugin/api/spi/AbstractProtocolPlugin.java @@ -0,0 +1,15 @@ +package io.metersphere.plugin.api.spi; + +/** + * 接口协议插件抽象类 + * @Author: jianxing + * @CreateTime: 2023-11-06 11:10 + */ +public abstract class AbstractProtocolPlugin extends AbstractApiPlugin { + + /** + * 返回协议名称 + * @return + */ + abstract public String getProtocol(); +} diff --git a/backend/framework/sdk/src/main/java/io/metersphere/sdk/constants/PermissionConstants.java b/backend/framework/sdk/src/main/java/io/metersphere/sdk/constants/PermissionConstants.java index b6ac21090a..9052c1ce80 100644 --- a/backend/framework/sdk/src/main/java/io/metersphere/sdk/constants/PermissionConstants.java +++ b/backend/framework/sdk/src/main/java/io/metersphere/sdk/constants/PermissionConstants.java @@ -222,11 +222,11 @@ public class PermissionConstants { /*------ start: API_DEBUG ------*/ public static final String PROJECT_API_DEBUG_READ = "PROJECT_API_DEBUG:READ"; - public static final String PROJECT_API_DEBUG_READ_ADD = "PROJECT_API_DEBUG:READ+ADD"; - public static final String PROJECT_API_DEBUG_READ_UPDATE = "PROJECT_API_DEBUG:READ+UPDATE"; - public static final String PROJECT_API_DEBUG_READ_DELETE = "PROJECT_API_DEBUG:READ+DELETE"; - public static final String PROJECT_API_DEBUG_READ_IMPORT = "PROJECT_API_DEBUG:READ+IMPORT"; - public static final String PROJECT_API_DEBUG_READ_EXECUTE = "PROJECT_API_DEBUG:READ+EXECUTE"; + public static final String PROJECT_API_DEBUG_ADD = "PROJECT_API_DEBUG:READ+ADD"; + public static final String PROJECT_API_DEBUG_UPDATE = "PROJECT_API_DEBUG:READ+UPDATE"; + public static final String PROJECT_API_DEBUG_DELETE = "PROJECT_API_DEBUG:READ+DELETE"; + public static final String PROJECT_API_DEBUG_IMPORT = "PROJECT_API_DEBUG:READ+IMPORT"; + public static final String PROJECT_API_DEBUG_EXECUTE = "PROJECT_API_DEBUG:READ+EXECUTE"; /*------ end: API_DEBUG ------*/ /*------ start: BUG ------*/ diff --git a/backend/framework/sdk/src/main/java/io/metersphere/sdk/dto/api/request/http/Header.java b/backend/framework/sdk/src/main/java/io/metersphere/sdk/dto/api/request/http/Header.java new file mode 100644 index 0000000000..1ad8859a6b --- /dev/null +++ b/backend/framework/sdk/src/main/java/io/metersphere/sdk/dto/api/request/http/Header.java @@ -0,0 +1,12 @@ +package io.metersphere.sdk.dto.api.request.http; + +import lombok.Data; + +/** + * @Author: jianxing + * @CreateTime: 2023-11-06 16:59 + */ +@Data +public class Header extends KeyValueParam { + +} diff --git a/backend/framework/sdk/src/main/java/io/metersphere/sdk/dto/api/request/http/KeyValueParam.java b/backend/framework/sdk/src/main/java/io/metersphere/sdk/dto/api/request/http/KeyValueParam.java new file mode 100644 index 0000000000..757a7e320d --- /dev/null +++ b/backend/framework/sdk/src/main/java/io/metersphere/sdk/dto/api/request/http/KeyValueParam.java @@ -0,0 +1,27 @@ +package io.metersphere.sdk.dto.api.request.http; + +import lombok.Data; + +/** + * @Author: jianxing + * @CreateTime: 2023-11-06 17:27 + */ +@Data +public class KeyValueParam { + /** + * 键 + */ + private String key; + /** + * 值 + */ + private String value; + /** + * 是否启用 + */ + private Boolean enable = true; + /** + * 描述 + */ + private String description; +} diff --git a/backend/framework/sdk/src/main/java/io/metersphere/sdk/dto/api/request/http/MsHTTPConfig.java b/backend/framework/sdk/src/main/java/io/metersphere/sdk/dto/api/request/http/MsHTTPConfig.java new file mode 100644 index 0000000000..634a9c5e01 --- /dev/null +++ b/backend/framework/sdk/src/main/java/io/metersphere/sdk/dto/api/request/http/MsHTTPConfig.java @@ -0,0 +1,16 @@ +package io.metersphere.sdk.dto.api.request.http; + +import lombok.Data; + +/** + * @Author: jianxing + * @CreateTime: 2023-11-07 10:47 + */ +@Data +public class MsHTTPConfig { + private Long connectTimeout; + private Long responseTimeout; + private String certificateAlias; + private Boolean followRedirects = true; + private Boolean autoRedirects = false; +} diff --git a/backend/framework/sdk/src/main/java/io/metersphere/sdk/dto/api/request/http/MsHTTPElement.java b/backend/framework/sdk/src/main/java/io/metersphere/sdk/dto/api/request/http/MsHTTPElement.java index 06b19ac863..fdfb3231a6 100644 --- a/backend/framework/sdk/src/main/java/io/metersphere/sdk/dto/api/request/http/MsHTTPElement.java +++ b/backend/framework/sdk/src/main/java/io/metersphere/sdk/dto/api/request/http/MsHTTPElement.java @@ -1,11 +1,62 @@ package io.metersphere.sdk.dto.api.request.http; import io.metersphere.plugin.api.spi.AbstractMsTestElement; +import io.metersphere.sdk.dto.api.request.http.auth.HTTPAuth; +import io.metersphere.sdk.dto.api.request.http.body.Body; +import io.metersphere.sdk.dto.api.request.processors.MsProcessor; import lombok.Data; import lombok.EqualsAndHashCode; +import java.util.List; + + @Data @EqualsAndHashCode(callSuper = true) public class MsHTTPElement extends AbstractMsTestElement { - private String domain; + // todo 完善字段校验 + /** + * 完整请求地址 + */ + private String url; + /** + * 接口定义和用例的请求路径 + */ + private String path; + /** + * 请求方法 + */ + private String method; + /** + * 请求体 + */ + private Body body; + /** + * 请求头 + */ + private List
headers; + /** + * rest参数 + */ + private List rest; + /** + * query参数 + */ + private List query; + /** + * 其他配置 + */ + private MsHTTPConfig otherConfig; + /** + * 认证配置 + */ + private HTTPAuth authConfig; + /** + * 前置处理器 + */ + private List preProcessors; + /** + * 后置处理器 + */ + private List postProcessors; + // todo 断言和提取 待设计 } \ No newline at end of file diff --git a/backend/framework/sdk/src/main/java/io/metersphere/sdk/dto/api/request/http/QueryParam.java b/backend/framework/sdk/src/main/java/io/metersphere/sdk/dto/api/request/http/QueryParam.java new file mode 100644 index 0000000000..7b68be2979 --- /dev/null +++ b/backend/framework/sdk/src/main/java/io/metersphere/sdk/dto/api/request/http/QueryParam.java @@ -0,0 +1,22 @@ +package io.metersphere.sdk.dto.api.request.http; + +import lombok.Data; + +/** + * @Author: jianxing + * @CreateTime: 2023-11-06 16:59 + */ +@Data +public class QueryParam extends KeyValueParam { + + /** + * 参数类型 + * 默认string,可选integer、number、array + * todo + */ + private String paramType; + /** + * 是否必填 + */ + private Boolean required = false; +} diff --git a/backend/framework/sdk/src/main/java/io/metersphere/sdk/dto/api/request/http/RestParam.java b/backend/framework/sdk/src/main/java/io/metersphere/sdk/dto/api/request/http/RestParam.java new file mode 100644 index 0000000000..6aab61368e --- /dev/null +++ b/backend/framework/sdk/src/main/java/io/metersphere/sdk/dto/api/request/http/RestParam.java @@ -0,0 +1,21 @@ +package io.metersphere.sdk.dto.api.request.http; + +import lombok.Data; + +/** + * @Author: jianxing + * @CreateTime: 2023-11-06 16:59 + */ +@Data +public class RestParam extends KeyValueParam { + /** + * 参数类型 + * 默认string,可选integer、number、array + * todo + */ + private String paramType; + /** + * 是否必填 + */ + private Boolean required = false; +} diff --git a/backend/framework/sdk/src/main/java/io/metersphere/sdk/dto/api/request/http/auth/BasicAuth.java b/backend/framework/sdk/src/main/java/io/metersphere/sdk/dto/api/request/http/auth/BasicAuth.java new file mode 100644 index 0000000000..16ba1b5498 --- /dev/null +++ b/backend/framework/sdk/src/main/java/io/metersphere/sdk/dto/api/request/http/auth/BasicAuth.java @@ -0,0 +1,15 @@ +package io.metersphere.sdk.dto.api.request.http.auth; + +import com.fasterxml.jackson.annotation.JsonTypeName; +import lombok.Data; + +/** + * @Author: jianxing + * @CreateTime: 2023-11-07 11:00 + */ +@Data +@JsonTypeName("BASIC") +public class BasicAuth extends HTTPAuth { + private String userName; + private String password; +} diff --git a/backend/framework/sdk/src/main/java/io/metersphere/sdk/dto/api/request/http/auth/DigestAuth.java b/backend/framework/sdk/src/main/java/io/metersphere/sdk/dto/api/request/http/auth/DigestAuth.java new file mode 100644 index 0000000000..9ba22e46f2 --- /dev/null +++ b/backend/framework/sdk/src/main/java/io/metersphere/sdk/dto/api/request/http/auth/DigestAuth.java @@ -0,0 +1,15 @@ +package io.metersphere.sdk.dto.api.request.http.auth; + +import com.fasterxml.jackson.annotation.JsonTypeName; +import lombok.Data; + +/** + * @Author: jianxing + * @CreateTime: 2023-11-07 11:00 + */ +@Data +@JsonTypeName("DIGEST") +public class DigestAuth extends HTTPAuth { + private String userName; + private String password; +} diff --git a/backend/framework/sdk/src/main/java/io/metersphere/sdk/dto/api/request/http/auth/HTTPAuth.java b/backend/framework/sdk/src/main/java/io/metersphere/sdk/dto/api/request/http/auth/HTTPAuth.java new file mode 100644 index 0000000000..e3d81ba477 --- /dev/null +++ b/backend/framework/sdk/src/main/java/io/metersphere/sdk/dto/api/request/http/auth/HTTPAuth.java @@ -0,0 +1,19 @@ +package io.metersphere.sdk.dto.api.request.http.auth; + +import com.fasterxml.jackson.annotation.JsonSubTypes; +import com.fasterxml.jackson.annotation.JsonTypeInfo; +import lombok.Data; + +/** + * @Author: jianxing + * @CreateTime: 2023-11-07 11:00 + */ +@Data +@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property = "authType") +@JsonSubTypes({ + @JsonSubTypes.Type(value = NoAuth.class), + @JsonSubTypes.Type(value = BasicAuth.class), + @JsonSubTypes.Type(value = DigestAuth.class), +}) +public abstract class HTTPAuth { +} diff --git a/backend/framework/sdk/src/main/java/io/metersphere/sdk/dto/api/request/http/auth/NoAuth.java b/backend/framework/sdk/src/main/java/io/metersphere/sdk/dto/api/request/http/auth/NoAuth.java new file mode 100644 index 0000000000..c4417476ba --- /dev/null +++ b/backend/framework/sdk/src/main/java/io/metersphere/sdk/dto/api/request/http/auth/NoAuth.java @@ -0,0 +1,13 @@ +package io.metersphere.sdk.dto.api.request.http.auth; + +import com.fasterxml.jackson.annotation.JsonTypeName; +import lombok.Data; + +/** + * @Author: jianxing + * @CreateTime: 2023-11-07 11:00 + */ +@Data +@JsonTypeName("NONE") +public class NoAuth extends HTTPAuth { +} diff --git a/backend/framework/sdk/src/main/java/io/metersphere/sdk/dto/api/request/http/body/BinaryBody.java b/backend/framework/sdk/src/main/java/io/metersphere/sdk/dto/api/request/http/body/BinaryBody.java new file mode 100644 index 0000000000..2cd57c97ef --- /dev/null +++ b/backend/framework/sdk/src/main/java/io/metersphere/sdk/dto/api/request/http/body/BinaryBody.java @@ -0,0 +1,17 @@ +package io.metersphere.sdk.dto.api.request.http.body; + +import com.fasterxml.jackson.annotation.JsonTypeName; +import lombok.Data; + +/** + * @Author: jianxing + * @CreateTime: 2023-11-06 18:25 + */ +@Data +@JsonTypeName("BINARY") +public class BinaryBody extends Body { + // todo 如果fileName能直接定义到文件,就不需要filePath + private String filePath; + private String fileName; + private String description; +} diff --git a/backend/framework/sdk/src/main/java/io/metersphere/sdk/dto/api/request/http/body/Body.java b/backend/framework/sdk/src/main/java/io/metersphere/sdk/dto/api/request/http/body/Body.java new file mode 100644 index 0000000000..c8e46def94 --- /dev/null +++ b/backend/framework/sdk/src/main/java/io/metersphere/sdk/dto/api/request/http/body/Body.java @@ -0,0 +1,23 @@ +package io.metersphere.sdk.dto.api.request.http.body; + +import com.fasterxml.jackson.annotation.JsonSubTypes; +import com.fasterxml.jackson.annotation.JsonTypeInfo; +import lombok.Data; + +/** + * @Author: jianxing + * @CreateTime: 2023-11-06 16:59 + */ +@Data +@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property = "bodyType") +@JsonSubTypes({ + @JsonSubTypes.Type(value = NoneBody.class), + @JsonSubTypes.Type(value = FormDataBody.class), + @JsonSubTypes.Type(value = WWWFormBody.class), + @JsonSubTypes.Type(value = JsonBody.class), + @JsonSubTypes.Type(value = XmlBody.class), + @JsonSubTypes.Type(value = RawBody.class), + @JsonSubTypes.Type(value = BinaryBody.class) +}) +public abstract class Body { +} diff --git a/backend/framework/sdk/src/main/java/io/metersphere/sdk/dto/api/request/http/body/FormDataBody.java b/backend/framework/sdk/src/main/java/io/metersphere/sdk/dto/api/request/http/body/FormDataBody.java new file mode 100644 index 0000000000..ea5adb5466 --- /dev/null +++ b/backend/framework/sdk/src/main/java/io/metersphere/sdk/dto/api/request/http/body/FormDataBody.java @@ -0,0 +1,16 @@ +package io.metersphere.sdk.dto.api.request.http.body; + +import com.fasterxml.jackson.annotation.JsonTypeName; +import lombok.Data; + +import java.util.List; + +/** + * @Author: jianxing + * @CreateTime: 2023-11-06 16:59 + */ +@Data +@JsonTypeName("FORM_DATA") +public class FormDataBody extends Body { + private List fromValues; +} diff --git a/backend/framework/sdk/src/main/java/io/metersphere/sdk/dto/api/request/http/body/FormDataKV.java b/backend/framework/sdk/src/main/java/io/metersphere/sdk/dto/api/request/http/body/FormDataKV.java new file mode 100644 index 0000000000..b48e519e09 --- /dev/null +++ b/backend/framework/sdk/src/main/java/io/metersphere/sdk/dto/api/request/http/body/FormDataKV.java @@ -0,0 +1,18 @@ +package io.metersphere.sdk.dto.api.request.http.body; + +import io.metersphere.sdk.dto.api.request.http.KeyValueParam; +import lombok.Data; + +/** + * @Author: jianxing + * @CreateTime: 2023-11-06 18:11 + */ +@Data +public class FormDataKV extends KeyValueParam { + private String paramType; + private Boolean required = false; + private Integer minLength; + private Integer maxLength; + private String contentType; + private Boolean encode = false; +} diff --git a/backend/framework/sdk/src/main/java/io/metersphere/sdk/dto/api/request/http/body/JsonBody.java b/backend/framework/sdk/src/main/java/io/metersphere/sdk/dto/api/request/http/body/JsonBody.java new file mode 100644 index 0000000000..493a1fb790 --- /dev/null +++ b/backend/framework/sdk/src/main/java/io/metersphere/sdk/dto/api/request/http/body/JsonBody.java @@ -0,0 +1,14 @@ +package io.metersphere.sdk.dto.api.request.http.body; + +import com.fasterxml.jackson.annotation.JsonTypeName; +import lombok.Data; + +/** + * @Author: jianxing + * @CreateTime: 2023-11-06 18:25 + */ +@Data +@JsonTypeName("JSON") +public class JsonBody extends Body { + private String value; +} diff --git a/backend/framework/sdk/src/main/java/io/metersphere/sdk/dto/api/request/http/body/NoneBody.java b/backend/framework/sdk/src/main/java/io/metersphere/sdk/dto/api/request/http/body/NoneBody.java new file mode 100644 index 0000000000..556cf18e11 --- /dev/null +++ b/backend/framework/sdk/src/main/java/io/metersphere/sdk/dto/api/request/http/body/NoneBody.java @@ -0,0 +1,13 @@ +package io.metersphere.sdk.dto.api.request.http.body; + +import com.fasterxml.jackson.annotation.JsonTypeName; +import lombok.Data; + +/** + * @Author: jianxing + * @CreateTime: 2023-11-06 18:25 + */ +@Data +@JsonTypeName("NONE") +public class NoneBody extends Body { +} diff --git a/backend/framework/sdk/src/main/java/io/metersphere/sdk/dto/api/request/http/body/RawBody.java b/backend/framework/sdk/src/main/java/io/metersphere/sdk/dto/api/request/http/body/RawBody.java new file mode 100644 index 0000000000..ba819b4924 --- /dev/null +++ b/backend/framework/sdk/src/main/java/io/metersphere/sdk/dto/api/request/http/body/RawBody.java @@ -0,0 +1,14 @@ +package io.metersphere.sdk.dto.api.request.http.body; + +import com.fasterxml.jackson.annotation.JsonTypeName; +import lombok.Data; + +/** + * @Author: jianxing + * @CreateTime: 2023-11-06 18:25 + */ +@Data +@JsonTypeName("RAW") +public class RawBody extends Body { + private String value; +} diff --git a/backend/framework/sdk/src/main/java/io/metersphere/sdk/dto/api/request/http/body/WWWFormBody.java b/backend/framework/sdk/src/main/java/io/metersphere/sdk/dto/api/request/http/body/WWWFormBody.java new file mode 100644 index 0000000000..d590333fc8 --- /dev/null +++ b/backend/framework/sdk/src/main/java/io/metersphere/sdk/dto/api/request/http/body/WWWFormBody.java @@ -0,0 +1,16 @@ +package io.metersphere.sdk.dto.api.request.http.body; + +import com.fasterxml.jackson.annotation.JsonTypeName; +import lombok.Data; + +import java.util.List; + +/** + * @Author: jianxing + * @CreateTime: 2023-11-06 16:59 + */ +@Data +@JsonTypeName("WWW_FORM") +public class WWWFormBody extends Body { + private List fromValues; +} diff --git a/backend/framework/sdk/src/main/java/io/metersphere/sdk/dto/api/request/http/body/XmlBody.java b/backend/framework/sdk/src/main/java/io/metersphere/sdk/dto/api/request/http/body/XmlBody.java new file mode 100644 index 0000000000..7a9286d002 --- /dev/null +++ b/backend/framework/sdk/src/main/java/io/metersphere/sdk/dto/api/request/http/body/XmlBody.java @@ -0,0 +1,14 @@ +package io.metersphere.sdk.dto.api.request.http.body; + +import com.fasterxml.jackson.annotation.JsonTypeName; +import lombok.Data; + +/** + * @Author: jianxing + * @CreateTime: 2023-11-06 18:25 + */ +@Data +@JsonTypeName("XML") +public class XmlBody extends Body { + private String value; +} diff --git a/backend/framework/sdk/src/main/java/io/metersphere/sdk/dto/api/request/processors/MsProcessor.java b/backend/framework/sdk/src/main/java/io/metersphere/sdk/dto/api/request/processors/MsProcessor.java new file mode 100644 index 0000000000..e602e29cd1 --- /dev/null +++ b/backend/framework/sdk/src/main/java/io/metersphere/sdk/dto/api/request/processors/MsProcessor.java @@ -0,0 +1,19 @@ +package io.metersphere.sdk.dto.api.request.processors; + +import com.fasterxml.jackson.annotation.JsonSubTypes; +import com.fasterxml.jackson.annotation.JsonTypeInfo; +import lombok.Data; + +/** + * @Author: jianxing + * @CreateTime: 2023-11-07 10:17 + */ +@Data +@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property = "processorType") +@JsonSubTypes({ + @JsonSubTypes.Type(value = ScriptProcessor.class), + @JsonSubTypes.Type(value = SQLProcessor.class), + @JsonSubTypes.Type(value = TimeWaitingProcessor.class), +}) +public abstract class MsProcessor { +} diff --git a/backend/framework/sdk/src/main/java/io/metersphere/sdk/dto/api/request/processors/SQLProcessor.java b/backend/framework/sdk/src/main/java/io/metersphere/sdk/dto/api/request/processors/SQLProcessor.java new file mode 100644 index 0000000000..511d9b63e9 --- /dev/null +++ b/backend/framework/sdk/src/main/java/io/metersphere/sdk/dto/api/request/processors/SQLProcessor.java @@ -0,0 +1,48 @@ +package io.metersphere.sdk.dto.api.request.processors; + +import com.fasterxml.jackson.annotation.JsonTypeName; +import io.metersphere.sdk.dto.api.request.http.KeyValueParam; +import lombok.Data; + +import java.util.List; + +/** + * @Author: jianxing + * @CreateTime: 2023-11-06 21:12 + */ +@Data +@JsonTypeName("SQL") +public class SQLProcessor extends MsProcessor { + /** + * 脚本内容 + */ + private String script; + /** + * 超时时间 + */ + private long queryTimeout; + /** + * 存储结果 + */ + private String resultVariable; + /** + * 按列存储 + */ + private String variableNames; + /** + * 变量列表 + */ + private List variables; + /** + * 环境ID + */ + private String environmentId; + /** + * 数据源ID + */ + private String dataSourceId; + /** + * 是否启用 + */ + private Boolean enable; +} diff --git a/backend/framework/sdk/src/main/java/io/metersphere/sdk/dto/api/request/processors/ScriptProcessor.java b/backend/framework/sdk/src/main/java/io/metersphere/sdk/dto/api/request/processors/ScriptProcessor.java new file mode 100644 index 0000000000..c7f01cba59 --- /dev/null +++ b/backend/framework/sdk/src/main/java/io/metersphere/sdk/dto/api/request/processors/ScriptProcessor.java @@ -0,0 +1,17 @@ +package io.metersphere.sdk.dto.api.request.processors; + +import com.fasterxml.jackson.annotation.JsonTypeName; +import lombok.Data; + +/** + * @Author: jianxing + * @CreateTime: 2023-11-06 21:12 + */ +@Data +@JsonTypeName("SCRIPT") +public class ScriptProcessor extends MsProcessor { + private String script; + private String scriptLanguage; + private Boolean jsrEnable; + private Boolean enable; +} diff --git a/backend/framework/sdk/src/main/java/io/metersphere/sdk/dto/api/request/processors/TimeWaitingProcessor.java b/backend/framework/sdk/src/main/java/io/metersphere/sdk/dto/api/request/processors/TimeWaitingProcessor.java new file mode 100644 index 0000000000..bd7ee99a39 --- /dev/null +++ b/backend/framework/sdk/src/main/java/io/metersphere/sdk/dto/api/request/processors/TimeWaitingProcessor.java @@ -0,0 +1,15 @@ +package io.metersphere.sdk.dto.api.request.processors; + +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; + private Boolean enable; +} diff --git a/backend/framework/sdk/src/main/resources/i18n/api.properties b/backend/framework/sdk/src/main/resources/i18n/api.properties index 9e0e9db114..82615fde4b 100644 --- a/backend/framework/sdk/src/main/resources/i18n/api.properties +++ b/backend/framework/sdk/src/main/resources/i18n/api.properties @@ -283,3 +283,6 @@ api_environment_config.id.not_blank=ID不能为空 api_environment_config.environment_id.length_range=环境ID长度必须在1-50之间 api_environment_config.environment_id.not_blank=环境ID不能为空 api_module.not.exist=模块不存在 + +permission.api.name=接口测试 +api_debug_exist=接口已存在 \ No newline at end of file diff --git a/backend/framework/sdk/src/main/resources/i18n/api_en_US.properties b/backend/framework/sdk/src/main/resources/i18n/api_en_US.properties index 3bb689246b..f39dc8d7f1 100644 --- a/backend/framework/sdk/src/main/resources/i18n/api_en_US.properties +++ b/backend/framework/sdk/src/main/resources/i18n/api_en_US.properties @@ -267,6 +267,11 @@ api_debug.project_id.not_blank=Project ID cannot be blank api_debug.project_id.length_range=Project ID length must be between 1-50 api_debug.module_id.not_blank=Module ID cannot be blank api_debug.module_id.length_range=Module ID length must be between 1-50 +api_debug.create_user.not_blank=The creator cannot be empty +api_debug.create_user.length_range=Creator length must be between {min}-{max} +api_debug.update_user.not_blank=Modifier cannot be blank +api_debug.update_user.length_range=Modifier length must be between {min}-{max} + #module: ApiDebugModule api_debug_module.id.not_blank=ID cannot be blank api_debug_module.id.length_range=Module ID length must be between 1-50 @@ -282,7 +287,10 @@ api_debug_module.unplanned_request=Unplanned request api_unplanned_request=Unplanned Api #module: ApiEnvironmentConfig -api_environment_config.id.not_blank=ID不能为空 -api_environment_config.environment_id.length_range=环境ID长度必须在1-50之间 -api_environment_config.environment_id.not_blank=环境ID不能为空 -api_module.not.exist=模块不存在 \ No newline at end of file +api_environment_config.id.not_blank=ID cannot be blank +api_environment_config.environment_id.length_range=Environment ID length must be between 1-50 +api_environment_config.environment_id.not_blank=Environment ID cannot be blank +api_module.not.exist=The module does not exist + +permission.api.name=API Test +api_debug_exist=The API already exists \ No newline at end of file diff --git a/backend/framework/sdk/src/main/resources/i18n/api_zh_CN.properties b/backend/framework/sdk/src/main/resources/i18n/api_zh_CN.properties index e6cd648a1e..007b9064e6 100644 --- a/backend/framework/sdk/src/main/resources/i18n/api_zh_CN.properties +++ b/backend/framework/sdk/src/main/resources/i18n/api_zh_CN.properties @@ -267,6 +267,11 @@ api_debug.project_id.not_blank=项目ID不能为空 api_debug.project_id.length_range=项目ID长度必须在1-50之间 api_debug.module_id.not_blank=模块ID不能为空 api_debug.module_id.length_range=模块ID长度必须在1-50之间 +api_debug.create_user.not_blank=创建人不能为空 +api_debug.create_user.length_range=创建人长度必须在{min}和{max}之间 +api_debug.update_user.not_blank=修改人不能为空 +api_debug.update_user.length_range=修改人长度必须在{min}和{max}之间 + #module: ApiDebugModule api_debug_module.id.not_blank=ID不能为空 api_debug_module.id.length_range=模块ID长度必须在1-50之间 @@ -285,4 +290,7 @@ api_unplanned_request=未规划接口 api_environment_config.id.not_blank=ID不能为空 api_environment_config.environment_id.length_range=环境ID长度必须在1-50之间 api_environment_config.environment_id.not_blank=环境ID不能为空 -api_module.not.exist=模块不存在 \ No newline at end of file +api_module.not.exist=模块不存在 + +permission.api.name=接口测试 +api_debug_exist=接口已存在 \ No newline at end of file diff --git a/backend/framework/sdk/src/main/resources/i18n/api_zh_TW.properties b/backend/framework/sdk/src/main/resources/i18n/api_zh_TW.properties index 6b5cde4f5b..79da9342fc 100644 --- a/backend/framework/sdk/src/main/resources/i18n/api_zh_TW.properties +++ b/backend/framework/sdk/src/main/resources/i18n/api_zh_TW.properties @@ -267,6 +267,11 @@ api_debug.project_id.not_blank=項目ID不能為空 api_debug.project_id.length_range=項目ID長度必須在1-50之間 api_debug.module_id.not_blank=模塊ID不能為空 api_debug.module_id.length_range=模塊ID長度必須在1-50之間 +api_debug.create_user.not_blank=創建人不能為空 +api_debug.create_user.length_range=創建人長度必須在{min}和{max}之間 +api_debug.update_user.not_blank=修改人不能為空 +api_debug.update_user.length_range=修改人長度必須在{min}和{max}之間 + #module: ApiDebugModule api_debug_module.id.not_blank=ID不能為空 api_debug_module.id.length_range=模塊ID長度必須在1-50之間 @@ -285,4 +290,7 @@ api_unplanned_request=未规划接口 api_environment_config.id.not_blank=ID不能為空 api_environment_config.environment_id.length_range=環境ID長度必須在1-50之間 api_environment_config.environment_id.not_blank=環境ID不能為空 -api_module.not.exist=模塊不存在 \ No newline at end of file +api_module.not.exist=模塊不存在 + +permission.api.name=接口測試 +api_debug_exist=接口已存在 \ No newline at end of file diff --git a/backend/framework/sdk/src/main/resources/i18n/commons.properties b/backend/framework/sdk/src/main/resources/i18n/commons.properties index 6f7299fd74..f189fef36b 100644 --- a/backend/framework/sdk/src/main/resources/i18n/commons.properties +++ b/backend/framework/sdk/src/main/resources/i18n/commons.properties @@ -439,6 +439,7 @@ permission.delete=删除 permission.import=导入 permission.recover=恢复 permission.export=导出 +permission.execute=执行 file_name_illegal_error=文件名不合法 plugin_enable_error=插件未启用 diff --git a/backend/framework/sdk/src/main/resources/i18n/commons_en_US.properties b/backend/framework/sdk/src/main/resources/i18n/commons_en_US.properties index e838e9d6d1..feba1ceb56 100644 --- a/backend/framework/sdk/src/main/resources/i18n/commons_en_US.properties +++ b/backend/framework/sdk/src/main/resources/i18n/commons_en_US.properties @@ -441,6 +441,7 @@ permission.delete=Delete permission.import=Import permission.recover=Recover permission.export=Export +permission.execute=Execute file_name_illegal_error=File name is illegal plugin_enable_error=Plugin is not enabled diff --git a/backend/framework/sdk/src/main/resources/i18n/commons_zh_CN.properties b/backend/framework/sdk/src/main/resources/i18n/commons_zh_CN.properties index ead458f1b5..0b4cc4ee43 100644 --- a/backend/framework/sdk/src/main/resources/i18n/commons_zh_CN.properties +++ b/backend/framework/sdk/src/main/resources/i18n/commons_zh_CN.properties @@ -439,6 +439,7 @@ permission.delete=删除 permission.import=导入 permission.recover=恢复 permission.export=导出 +permission.execute=执行 file_name_illegal_error=文件名不合法 plugin_enable_error=插件未启用 diff --git a/backend/framework/sdk/src/main/resources/i18n/commons_zh_TW.properties b/backend/framework/sdk/src/main/resources/i18n/commons_zh_TW.properties index dbfd99f7c7..18be155441 100644 --- a/backend/framework/sdk/src/main/resources/i18n/commons_zh_TW.properties +++ b/backend/framework/sdk/src/main/resources/i18n/commons_zh_TW.properties @@ -437,6 +437,7 @@ permission.delete=刪除 permission.import=導入 permission.recover=恢復 permission.export=導出 +permission.execute=執行 file_name_illegal_error=文件名不合法 plugin_enable_error=插件未啟用 diff --git a/backend/services/api-test/src/main/java/io/metersphere/api/controller/debug/ApiDebugController.java b/backend/services/api-test/src/main/java/io/metersphere/api/controller/debug/ApiDebugController.java new file mode 100644 index 0000000000..0c829c29bf --- /dev/null +++ b/backend/services/api-test/src/main/java/io/metersphere/api/controller/debug/ApiDebugController.java @@ -0,0 +1,72 @@ +package io.metersphere.api.controller.debug; + +import io.metersphere.api.domain.ApiDebug; +import io.metersphere.api.dto.debug.ApiDebugAddRequest; +import io.metersphere.api.dto.debug.ApiDebugDTO; +import io.metersphere.api.dto.debug.ApiDebugSimpleDTO; +import io.metersphere.api.dto.debug.ApiDebugUpdateRequest; +import io.metersphere.api.service.debug.ApiDebugLogService; +import io.metersphere.api.service.debug.ApiDebugService; +import io.metersphere.sdk.constants.PermissionConstants; +import io.metersphere.system.log.annotation.Log; +import io.metersphere.system.log.constants.OperationLogType; +import io.metersphere.system.utils.SessionUtils; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.annotation.Resource; +import org.apache.shiro.authz.annotation.RequiresPermissions; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import java.util.List; + +/** + * @author : jianxing + * @date : 2023-11-6 + */ +@RestController +@RequestMapping("/api/debug") +@Tag(name = "接口调试") +public class ApiDebugController { + + @Resource + private ApiDebugService apiDebugService; + + @GetMapping("/list/{protocol}") + @Operation(summary = "获取接口调试列表") + @RequiresPermissions(PermissionConstants.PROJECT_API_DEBUG_READ) + public List list(@PathVariable String protocol) { + return apiDebugService.list(protocol, SessionUtils.getUserId()); + } + + @GetMapping("/get/{id}") + @Operation(summary = "获取接口调试详情") + @RequiresPermissions(PermissionConstants.PROJECT_API_DEBUG_READ) + public ApiDebugDTO get(@PathVariable String id) { + return apiDebugService.get(id); + } + + @PostMapping("/add") + @Operation(summary = "创建接口调试") + @RequiresPermissions(PermissionConstants.PROJECT_API_DEBUG_ADD) + @Log(type = OperationLogType.ADD, expression = "#msClass.addLog(#request)", msClass = ApiDebugLogService.class) + public ApiDebug add(@Validated @RequestBody ApiDebugAddRequest request) { + return apiDebugService.add(request, SessionUtils.getUserId()); + } + + @PostMapping("/update") + @Operation(summary = "更新接口调试") + @RequiresPermissions(PermissionConstants.PROJECT_API_DEBUG_UPDATE) + @Log(type = OperationLogType.UPDATE, expression = "#msClass.updateLog(#request)", msClass = ApiDebugLogService.class) + public ApiDebug update(@Validated @RequestBody ApiDebugUpdateRequest request) {; + return apiDebugService.update(request, SessionUtils.getUserId()); + } + + @GetMapping("/delete/{id}") + @Operation(summary = "删除接口调试") + @RequiresPermissions(PermissionConstants.PROJECT_API_DEBUG_DELETE) + @Log(type = OperationLogType.DELETE, expression = "#msClass.deleteLog(#id)", msClass = ApiDebugLogService.class) + public void delete(@PathVariable String id) { + apiDebugService.delete(id); + } +} \ No newline at end of file diff --git a/backend/services/api-test/src/main/java/io/metersphere/api/controller/debug/ApiDebugModuleController.java b/backend/services/api-test/src/main/java/io/metersphere/api/controller/debug/ApiDebugModuleController.java index 08b130bbb6..983ab7aaac 100644 --- a/backend/services/api-test/src/main/java/io/metersphere/api/controller/debug/ApiDebugModuleController.java +++ b/backend/services/api-test/src/main/java/io/metersphere/api/controller/debug/ApiDebugModuleController.java @@ -35,14 +35,14 @@ public class ApiDebugModuleController { @PostMapping("/add") @Operation(summary = "接口测试-接口调试-模块-添加模块") - @RequiresPermissions(PermissionConstants.PROJECT_API_DEBUG_READ_ADD) + @RequiresPermissions(PermissionConstants.PROJECT_API_DEBUG_ADD) public String add(@RequestBody @Validated ModuleCreateRequest request) { return apiDebugModuleService.add(request, SessionUtils.getUserId()); } @PostMapping("/update") @Operation(summary = "接口测试-接口调试-模块-修改模块") - @RequiresPermissions(PermissionConstants.PROJECT_API_DEBUG_READ_UPDATE) + @RequiresPermissions(PermissionConstants.PROJECT_API_DEBUG_UPDATE) public boolean list(@RequestBody @Validated ModuleUpdateRequest request) { apiDebugModuleService.update(request, SessionUtils.getUserId(), SessionUtils.getCurrentProjectId()); return true; @@ -50,14 +50,14 @@ public class ApiDebugModuleController { @GetMapping("/delete/{deleteId}") @Operation(summary = "接口测试-接口调试-模块-删除模块") - @RequiresPermissions(PermissionConstants.PROJECT_API_DEBUG_READ_DELETE) + @RequiresPermissions(PermissionConstants.PROJECT_API_DEBUG_DELETE) public void deleteNode(@PathVariable String deleteId) { apiDebugModuleService.deleteModule(deleteId, SessionUtils.getUserId()); } @PostMapping("/move") @Operation(summary = "接口测试-接口调试-模块-移动模块") - @RequiresPermissions(PermissionConstants.PROJECT_API_DEBUG_READ_UPDATE) + @RequiresPermissions(PermissionConstants.PROJECT_API_DEBUG_UPDATE) public void moveNode(@Validated @RequestBody NodeMoveRequest request) { apiDebugModuleService.moveNode(request, SessionUtils.getUserId()); } diff --git a/backend/services/api-test/src/main/java/io/metersphere/api/controller/result/ApiResultCode.java b/backend/services/api-test/src/main/java/io/metersphere/api/controller/result/ApiResultCode.java new file mode 100644 index 0000000000..b117b9e5cb --- /dev/null +++ b/backend/services/api-test/src/main/java/io/metersphere/api/controller/result/ApiResultCode.java @@ -0,0 +1,30 @@ +package io.metersphere.api.controller.result; + +import io.metersphere.sdk.exception.IResultCode; + +/** + * @author jianxing + */ +public enum ApiResultCode implements IResultCode { + + API_DEBUG_EXIST(104001, "api_debug_exist"); + + + private final int code; + private final String message; + + ApiResultCode(int code, String message) { + this.code = code; + this.message = message; + } + + @Override + public int getCode() { + return code; + } + + @Override + public String getMessage() { + return getTranslationMessage(this.message); + } +} diff --git a/backend/services/api-test/src/main/java/io/metersphere/api/dto/debug/ApiDebugAddRequest.java b/backend/services/api-test/src/main/java/io/metersphere/api/dto/debug/ApiDebugAddRequest.java new file mode 100644 index 0000000000..a22561fa4d --- /dev/null +++ b/backend/services/api-test/src/main/java/io/metersphere/api/dto/debug/ApiDebugAddRequest.java @@ -0,0 +1,48 @@ +package io.metersphere.api.dto.debug; + +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.Size; +import lombok.Data; + +import java.io.Serializable; + +/** + * @Author: jianxing + * @CreateTime: 2023-11-06 10:22 + */ +@Data +public class ApiDebugAddRequest implements Serializable { + + private static final long serialVersionUID = 1L; + + @Schema(description = "接口名称", requiredMode = Schema.RequiredMode.REQUIRED) + @NotBlank(message = "{api_debug.name.not_blank}") + @Size(min = 1, max = 255, message = "{api_debug.name.length_range}") + private String name; + + @Schema(description = "接口协议", requiredMode = Schema.RequiredMode.REQUIRED) + @NotBlank(message = "{api_debug.protocol.not_blank}") + @Size(min = 1, max = 20, message = "{api_debug.protocol.length_range}") + private String protocol; + + @Schema(description = "http协议类型post/get/其它协议则是协议名(mqtt)") + private String method; + + @Schema(description = "http协议url/其它协议则为空") + private String path; + + @Schema(description = "项目fk", requiredMode = Schema.RequiredMode.REQUIRED) + @NotBlank(message = "{api_debug.project_id.not_blank}") + @Size(min = 1, max = 50, message = "{api_debug.project_id.length_range}") + private String projectId; + + @Schema(description = "模块fk", requiredMode = Schema.RequiredMode.REQUIRED) + @NotBlank(message = "{api_debug.module_id.not_blank}") + @Size(min = 1, max = 50, message = "{api_debug.module_id.length_range}") + private String moduleId; + + @Schema(description = "请求内容") + @NotBlank + private String request; +} diff --git a/backend/services/api-test/src/main/java/io/metersphere/api/dto/debug/ApiDebugDTO.java b/backend/services/api-test/src/main/java/io/metersphere/api/dto/debug/ApiDebugDTO.java new file mode 100644 index 0000000000..b8187f122d --- /dev/null +++ b/backend/services/api-test/src/main/java/io/metersphere/api/dto/debug/ApiDebugDTO.java @@ -0,0 +1,15 @@ +package io.metersphere.api.dto.debug; + +import io.metersphere.api.domain.ApiDebug; +import io.metersphere.plugin.api.spi.AbstractMsTestElement; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +@Data +public class ApiDebugDTO extends ApiDebug { + @Schema(description = "请求内容") + private AbstractMsTestElement request; + + @Schema(description = "响应内容") + private String response; +} diff --git a/backend/services/api-test/src/main/java/io/metersphere/api/dto/debug/ApiDebugSimpleDTO.java b/backend/services/api-test/src/main/java/io/metersphere/api/dto/debug/ApiDebugSimpleDTO.java new file mode 100644 index 0000000000..6501eae61f --- /dev/null +++ b/backend/services/api-test/src/main/java/io/metersphere/api/dto/debug/ApiDebugSimpleDTO.java @@ -0,0 +1,19 @@ +package io.metersphere.api.dto.debug; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +@Data +public class ApiDebugSimpleDTO { + @Schema(description = "接口ID") + private String id; + + @Schema(description = "接口名称") + private String name; + + @Schema(description = "http协议类型post/get/其它协议则是协议名(mqtt)") + private String method; + + @Schema(description = "模块fk") + private String moduleId; +} diff --git a/backend/services/api-test/src/main/java/io/metersphere/api/dto/debug/ApiDebugUpdateRequest.java b/backend/services/api-test/src/main/java/io/metersphere/api/dto/debug/ApiDebugUpdateRequest.java new file mode 100644 index 0000000000..09393b2b71 --- /dev/null +++ b/backend/services/api-test/src/main/java/io/metersphere/api/dto/debug/ApiDebugUpdateRequest.java @@ -0,0 +1,45 @@ +package io.metersphere.api.dto.debug; + +import io.metersphere.validation.groups.Created; +import io.metersphere.validation.groups.Updated; +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.Size; +import lombok.Data; + +import java.io.Serializable; + +/** + * @Author: jianxing + * @CreateTime: 2023-11-06 10:22 + */ +@Data +public class ApiDebugUpdateRequest implements Serializable { + + private static final long serialVersionUID = 1L; + + @Schema(description = "接口pk", requiredMode = Schema.RequiredMode.REQUIRED) + @NotBlank(message = "{api_debug.id.not_blank}", groups = {Updated.class}) + @Size(min = 1, max = 50, message = "{api_debug.id.length_range}", groups = {Created.class, Updated.class}) + private String id; + + @Schema(description = "接口名称", requiredMode = Schema.RequiredMode.REQUIRED) + @NotBlank(message = "{api_debug.name.not_blank}", groups = {Created.class}) + @Size(min = 1, max = 255, message = "{api_debug.name.length_range}", groups = {Created.class, Updated.class}) + private String name; + + @Schema(description = "http协议类型post/get/其它协议则是协议名(mqtt)") + private String method; + + @Schema(description = "http协议路径/其它协议则为空") + private String path; + + @Schema(description = "模块fk", requiredMode = Schema.RequiredMode.REQUIRED) + @NotBlank(message = "{api_debug.module_id.not_blank}", groups = {Created.class}) + @Size(min = 1, max = 50, message = "{api_debug.module_id.length_range}", groups = {Created.class, Updated.class}) + private String moduleId; + + @Schema(description = "请求内容") + @NotBlank + private String request; +} diff --git a/backend/services/api-test/src/main/java/io/metersphere/api/mapper/ExtApiDebugMapper.java b/backend/services/api-test/src/main/java/io/metersphere/api/mapper/ExtApiDebugMapper.java new file mode 100644 index 0000000000..a0be062547 --- /dev/null +++ b/backend/services/api-test/src/main/java/io/metersphere/api/mapper/ExtApiDebugMapper.java @@ -0,0 +1,17 @@ +package io.metersphere.api.mapper; + + +import io.metersphere.api.dto.debug.ApiDebugSimpleDTO; +import org.apache.ibatis.annotations.Mapper; +import org.apache.ibatis.annotations.Param; + +import java.util.List; + +/** + * @author jianxing + * @date : 2023-11-6 + */ +@Mapper +public interface ExtApiDebugMapper { + List list(@Param("protocol") String protocol, @Param("userId") String userId); +} \ No newline at end of file diff --git a/backend/services/api-test/src/main/java/io/metersphere/api/mapper/ExtApiDebugMapper.xml b/backend/services/api-test/src/main/java/io/metersphere/api/mapper/ExtApiDebugMapper.xml new file mode 100644 index 0000000000..519abe1d49 --- /dev/null +++ b/backend/services/api-test/src/main/java/io/metersphere/api/mapper/ExtApiDebugMapper.xml @@ -0,0 +1,12 @@ + + + + + + + \ No newline at end of file diff --git a/backend/services/api-test/src/main/java/io/metersphere/api/service/debug/ApiDebugLogService.java b/backend/services/api-test/src/main/java/io/metersphere/api/service/debug/ApiDebugLogService.java new file mode 100644 index 0000000000..cd802924cc --- /dev/null +++ b/backend/services/api-test/src/main/java/io/metersphere/api/service/debug/ApiDebugLogService.java @@ -0,0 +1,68 @@ +package io.metersphere.api.service.debug; + +import io.metersphere.api.domain.ApiDebug; +import io.metersphere.api.dto.debug.ApiDebugAddRequest; +import io.metersphere.api.dto.debug.ApiDebugUpdateRequest; +import io.metersphere.sdk.constants.OperationLogConstants; +import io.metersphere.sdk.util.JSON; +import io.metersphere.system.log.constants.OperationLogModule; +import io.metersphere.system.log.constants.OperationLogType; +import io.metersphere.system.log.dto.LogDTO; +import jakarta.annotation.Resource; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +/** + * @author jianxing + * @date : 2023-11-6 + */ +@Service +@Transactional(rollbackFor = Exception.class) +public class ApiDebugLogService { + + @Resource + private ApiDebugService apiDebugService; + + public LogDTO addLog(ApiDebugAddRequest request) { + LogDTO dto = new LogDTO( + request.getProjectId(), + null, + null, + null, + OperationLogType.ADD.name(), + OperationLogModule.API_DEBUG, + request.getName()); + dto.setOriginalValue(JSON.toJSONBytes(request)); + return dto; + } + + public LogDTO updateLog(ApiDebugUpdateRequest request) { + ApiDebug apiDebug = apiDebugService.get(request.getId()); + LogDTO dto = null; + if (apiDebug != null) { + dto = new LogDTO( + apiDebug.getProjectId(), + null, + apiDebug.getId(), + null, + OperationLogType.UPDATE.name(), + OperationLogModule.API_DEBUG, + apiDebug.getName()); + dto.setOriginalValue(JSON.toJSONBytes(apiDebug)); + } + return dto; + } + + public LogDTO deleteLog(String id) { + ApiDebug apiDebug = apiDebugService.get(id); + LogDTO dto = new LogDTO( + OperationLogConstants.SYSTEM, + OperationLogConstants.SYSTEM, + apiDebug.getId(), + null, + OperationLogType.DELETE.name(), + OperationLogModule.API_DEBUG, + apiDebug.getName()); + dto.setOriginalValue(JSON.toJSONBytes(apiDebug)); + return dto; + } +} \ No newline at end of file diff --git a/backend/services/api-test/src/main/java/io/metersphere/api/service/debug/ApiDebugService.java b/backend/services/api-test/src/main/java/io/metersphere/api/service/debug/ApiDebugService.java new file mode 100644 index 0000000000..496e28b7f6 --- /dev/null +++ b/backend/services/api-test/src/main/java/io/metersphere/api/service/debug/ApiDebugService.java @@ -0,0 +1,133 @@ +package io.metersphere.api.service.debug; + +import io.metersphere.api.domain.ApiDebug; +import io.metersphere.api.domain.ApiDebugBlob; +import io.metersphere.api.domain.ApiDebugExample; +import io.metersphere.api.dto.debug.ApiDebugAddRequest; +import io.metersphere.api.dto.debug.ApiDebugDTO; +import io.metersphere.api.dto.debug.ApiDebugSimpleDTO; +import io.metersphere.api.dto.debug.ApiDebugUpdateRequest; +import io.metersphere.api.mapper.ApiDebugBlobMapper; +import io.metersphere.api.mapper.ApiDebugMapper; +import io.metersphere.api.mapper.ExtApiDebugMapper; +import io.metersphere.api.util.ApiDataUtils; +import io.metersphere.plugin.api.spi.AbstractMsTestElement; +import io.metersphere.project.service.ProjectService; +import io.metersphere.sdk.exception.MSException; +import io.metersphere.sdk.util.BeanUtils; +import io.metersphere.system.uid.IDGenerator; +import io.metersphere.system.utils.ServiceUtils; +import jakarta.annotation.Resource; +import org.apache.commons.collections.CollectionUtils; +import org.apache.commons.lang3.StringUtils; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.List; + +import static io.metersphere.api.controller.result.ApiResultCode.API_DEBUG_EXIST; + +/** + * @author jianxing + * @date : 2023-11-6 + */ +@Service +@Transactional(rollbackFor = Exception.class) +public class ApiDebugService { + + @Resource + private ApiDebugMapper apiDebugMapper; + @Resource + private ApiDebugBlobMapper apiDebugBlobMapper; + @Resource + private ExtApiDebugMapper extApiDebugMapper; + + public List list(String protocol, String userId) { + return extApiDebugMapper.list(protocol, userId); + } + + public ApiDebugDTO get(String id) { + checkResourceExist(id); + ApiDebug apiDebug = apiDebugMapper.selectByPrimaryKey(id); + ApiDebugBlob apiDebugBlob = apiDebugBlobMapper.selectByPrimaryKey(id); + ApiDebugDTO apiDebugDTO = new ApiDebugDTO(); + BeanUtils.copyBean(apiDebugDTO, apiDebug); + apiDebugDTO.setRequest(ApiDataUtils.parseObject(new String(apiDebugBlob.getRequest()), AbstractMsTestElement.class)); + apiDebugDTO.setResponse(apiDebugDTO.getResponse()); + return apiDebugDTO; + } + + public ApiDebug add(ApiDebugAddRequest request, String createUser) { + ProjectService.checkResourceExist(request.getProjectId()); + ApiDebug apiDebug = new ApiDebug(); + BeanUtils.copyBean(apiDebug, request); + apiDebug.setCreateUser(createUser); + checkAddExist(apiDebug); + apiDebug.setId(IDGenerator.nextStr()); + apiDebug.setCreateTime(System.currentTimeMillis()); + apiDebug.setUpdateTime(System.currentTimeMillis()); + apiDebug.setUpdateUser(apiDebug.getCreateUser()); + apiDebugMapper.insert(apiDebug); + + // todo 处理 body 文件 + // todo 校验 moduleId + + ApiDebugBlob apiDebugBlob = new ApiDebugBlob(); + apiDebugBlob.setId(apiDebug.getId()); + apiDebugBlob.setRequest(request.getRequest().getBytes()); + apiDebugBlobMapper.insert(apiDebugBlob); + return apiDebug; + } + + public ApiDebug update(ApiDebugUpdateRequest request, String updateUser) { + checkResourceExist(request.getId()); + ApiDebug apiDebug = BeanUtils.copyBean(new ApiDebug(), request); + ApiDebug originApiDebug = apiDebugMapper.selectByPrimaryKey(request.getId()); + checkUpdateExist(apiDebug, originApiDebug); + apiDebug.setUpdateUser(updateUser); + apiDebug.setUpdateTime(System.currentTimeMillis()); + apiDebugMapper.updateByPrimaryKeySelective(apiDebug); + + // todo 处理 body 文件 + // todo 校验 moduleId + + ApiDebugBlob apiDebugBlob = new ApiDebugBlob(); + apiDebugBlob.setId(request.getId()); + apiDebugBlob.setRequest(request.getRequest().getBytes()); + apiDebugBlobMapper.updateByPrimaryKeySelective(apiDebugBlob); + return apiDebug; + } + + public void delete(String id) { + checkResourceExist(id); + apiDebugMapper.deleteByPrimaryKey(id); + apiDebugBlobMapper.deleteByPrimaryKey(id); + } + + private void checkAddExist(ApiDebug apiDebug) { + ApiDebugExample example = new ApiDebugExample(); + example.createCriteria() + .andNameEqualTo(apiDebug.getName()) + .andModuleIdEqualTo(apiDebug.getModuleId()); + if (CollectionUtils.isNotEmpty(apiDebugMapper.selectByExample(example))) { + throw new MSException(API_DEBUG_EXIST); + } + } + + private void checkUpdateExist(ApiDebug apiDebug, ApiDebug originApiDebug) { + if (StringUtils.isBlank(apiDebug.getName())) { + return; + } + ApiDebugExample example = new ApiDebugExample(); + example.createCriteria() + .andIdNotEqualTo(apiDebug.getId()) + .andModuleIdEqualTo(apiDebug.getModuleId() == null ? originApiDebug.getModuleId() : apiDebug.getModuleId()) + .andNameEqualTo(apiDebug.getName()); + if (CollectionUtils.isNotEmpty(apiDebugMapper.selectByExample(example))) { + throw new MSException(API_DEBUG_EXIST); + } + } + private ApiDebug checkResourceExist(String id) { + return ServiceUtils.checkResourceExist(apiDebugMapper.selectByPrimaryKey(id), "permission.system_api_debug.name"); + } +} \ No newline at end of file diff --git a/backend/services/api-test/src/main/resources/permission.json b/backend/services/api-test/src/main/resources/permission.json index c037259f52..0bda75469f 100644 --- a/backend/services/api-test/src/main/resources/permission.json +++ b/backend/services/api-test/src/main/resources/permission.json @@ -21,12 +21,10 @@ "id": "PROJECT_API_DEBUG:READ+DELETE" }, { - "id": "PROJECT_API_DEBUG:READ+IMPORT", - "name": "permission.api_definition.import" + "id": "PROJECT_API_DEBUG:READ+IMPORT" }, { - "id": "PROJECT_API_DEBUG:READ+EXECUTE", - "name": "permission.api_definition.execute" + "id": "PROJECT_API_DEBUG:READ+EXECUTE" } ] }, @@ -47,16 +45,13 @@ "id": "PROJECT_API_DEFINITION:READ+DELETE" }, { - "id": "PROJECT_API_DEFINITION:READ+IMPORT", - "name": "permission.api_definition.import" + "id": "PROJECT_API_DEFINITION:READ+IMPORT" }, { - "id": "PROJECT_API_DEFINITION:READ+EXECUTE", - "name": "permission.api_definition.execute" + "id": "PROJECT_API_DEFINITION:READ+EXECUTE" }, { - "id": "PROJECT_API_DEFINITION:READ+EXPORT", - "name": "permission.api_definition.export" + "id": "PROJECT_API_DEFINITION:READ+EXPORT" } ] } diff --git a/backend/services/api-test/src/test/java/io/metersphere/api/controller/ApiDebugControllerTests.java b/backend/services/api-test/src/test/java/io/metersphere/api/controller/ApiDebugControllerTests.java new file mode 100644 index 0000000000..a8b1b3553f --- /dev/null +++ b/backend/services/api-test/src/test/java/io/metersphere/api/controller/ApiDebugControllerTests.java @@ -0,0 +1,189 @@ +package io.metersphere.api.controller; + +import io.metersphere.api.controller.param.ApiDebugAddRequestDefinition; +import io.metersphere.api.controller.param.ApiDebugUpdateRequestDefinition; +import io.metersphere.api.domain.ApiDebug; +import io.metersphere.api.domain.ApiDebugBlob; +import io.metersphere.api.dto.debug.ApiDebugAddRequest; +import io.metersphere.api.dto.debug.ApiDebugDTO; +import io.metersphere.api.dto.debug.ApiDebugSimpleDTO; +import io.metersphere.api.dto.debug.ApiDebugUpdateRequest; +import io.metersphere.api.mapper.ApiDebugBlobMapper; +import io.metersphere.api.mapper.ApiDebugMapper; +import io.metersphere.api.util.ApiDataUtils; +import io.metersphere.plugin.api.spi.AbstractMsTestElement; +import io.metersphere.sdk.constants.PermissionConstants; +import io.metersphere.sdk.dto.api.request.http.MsHTTPElement; +import io.metersphere.sdk.util.BeanUtils; +import io.metersphere.sdk.util.JSON; +import io.metersphere.system.base.BaseTest; +import io.metersphere.system.log.constants.OperationLogType; +import jakarta.annotation.Resource; +import org.junit.jupiter.api.*; +import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.test.web.servlet.MvcResult; + +import java.util.List; + +import static io.metersphere.api.controller.result.ApiResultCode.API_DEBUG_EXIST; +import static io.metersphere.system.controller.handler.result.MsHttpResultCode.NOT_FOUND; + +/** + * @author jianxing + * @date : 2023-11-7 + */ +@SpringBootTest(webEnvironment= SpringBootTest.WebEnvironment.RANDOM_PORT) +@AutoConfigureMockMvc +@TestMethodOrder(MethodOrderer.OrderAnnotation.class) +public class ApiDebugControllerTests extends BaseTest { + private static final String BASE_PATH = "/api/debug/"; + protected static final String DEFAULT_LIST = "list/{0}"; + protected static final String HTTP_PROTOCOL = "HTTP"; + + @Resource + private ApiDebugMapper apiDebugMapper; + @Resource + private ApiDebugBlobMapper apiDebugBlobMapper; + private static ApiDebug addApiDebug; + private static ApiDebug anotherAddApiDebug; + @Override + protected String getBasePath() { + return BASE_PATH; + } + + @Test + @Order(0) + public void listEmpty() throws Exception { + // @@校验没有数据的情况 + this.requestGetWithOk(DEFAULT_LIST, HTTP_PROTOCOL); + } + + @Test + @Order(1) + public void add() throws Exception { + // @@请求成功 + ApiDebugAddRequest request = new ApiDebugAddRequest(); + request.setPath("http://test.com"); + request.setMethod("GET"); + request.setName("test"); + request.setProtocol(HTTP_PROTOCOL); + request.setModuleId("default"); + request.setProjectId(DEFAULT_PROJECT_ID); + MsHTTPElement msHttpElement = MsHTTPElementTest.getMsHttpElement(); + request.setRequest(ApiDataUtils.toJSONString(msHttpElement)); + MvcResult mvcResult = this.requestPostWithOkAndReturn(DEFAULT_ADD, request); + // 校验请求成功数据 + ApiDebug resultData = getResultData(mvcResult, ApiDebug.class); + this.addApiDebug = assertUpdateApiDebug(request, msHttpElement, resultData.getId()); + + // 再插入一条数据,便于修改时重名校验 + request.setName("test1"); + mvcResult = this.requestPostWithOkAndReturn(DEFAULT_ADD, request); + resultData = getResultData(mvcResult, ApiDebug.class); + this.anotherAddApiDebug = assertUpdateApiDebug(request, msHttpElement, resultData.getId()); + + // @@重名校验异常 + assertErrorCode(this.requestPost(DEFAULT_ADD, request), API_DEBUG_EXIST); + // 校验项目是否存在 + request.setProjectId("111"); + assertErrorCode(this.requestPost(DEFAULT_ADD, request), NOT_FOUND); + + // @@校验日志 + checkLog(this.addApiDebug.getId(), OperationLogType.ADD); + // @@异常参数校验 + createdGroupParamValidateTest(ApiDebugAddRequestDefinition.class, DEFAULT_ADD); + // @@校验权限 + request.setProjectId(DEFAULT_PROJECT_ID); + requestPostPermissionTest(PermissionConstants.PROJECT_API_DEBUG_ADD, DEFAULT_ADD, request); + } + + private ApiDebug assertUpdateApiDebug(Object request, MsHTTPElement msHttpElement, String id) { + ApiDebug apiDebug = apiDebugMapper.selectByPrimaryKey(id); + ApiDebugBlob apiDebugBlob = apiDebugBlobMapper.selectByPrimaryKey(id); + ApiDebug copyApiDebug = BeanUtils.copyBean(new ApiDebug(), apiDebug); + copyApiDebug = BeanUtils.copyBean(copyApiDebug, request); + Assertions.assertEquals(apiDebug, copyApiDebug); + ApiDataUtils.setResolver(MsHTTPElement.class); + Assertions.assertEquals(msHttpElement, ApiDataUtils.parseObject(new String(apiDebugBlob.getRequest()), AbstractMsTestElement.class)); + return apiDebug; + } + + @Test + @Order(2) + public void update() throws Exception { + // @@请求成功 + ApiDebugUpdateRequest request = new ApiDebugUpdateRequest(); + request.setId(addApiDebug.getId()); + request.setPath("http://tesat.com"); + request.setName("test1"); + request.setMethod("POST"); + request.setModuleId("default1"); + MsHTTPElement msHttpElement = MsHTTPElementTest.getMsHttpElement(); + msHttpElement.setName("test1"); + request.setRequest(ApiDataUtils.toJSONString(msHttpElement)); + this.requestPostWithOk(DEFAULT_UPDATE, request); + // 校验请求成功数据 + assertUpdateApiDebug(request, msHttpElement, request.getId()); + + // @@重名校验异常 + request.setModuleId("default"); + assertErrorCode(this.requestPost(DEFAULT_UPDATE, request), API_DEBUG_EXIST); + + // @@校验日志 + checkLog(request.getId(), OperationLogType.UPDATE); + // @@异常参数校验 + updatedGroupParamValidateTest(ApiDebugUpdateRequestDefinition.class, DEFAULT_UPDATE); + // @@校验权限 + requestPostPermissionTest(PermissionConstants.PROJECT_API_DEBUG_UPDATE, DEFAULT_UPDATE, request); + } + @Test + @Order(3) + public void list() throws Exception { + // @@请求成功 + MvcResult mvcResult = this.requestGetWithOk(DEFAULT_LIST, HTTP_PROTOCOL) + .andReturn(); + // 校验数据是否正确 + List apiDebugList = getResultDataArray(mvcResult, ApiDebugSimpleDTO.class); + Assertions.assertEquals(apiDebugList.size(), 2); + Assertions.assertEquals(apiDebugList.get(0), BeanUtils.copyBean(new ApiDebugSimpleDTO(), + apiDebugMapper.selectByPrimaryKey(addApiDebug.getId()))); + Assertions.assertEquals(apiDebugList.get(1), + BeanUtils.copyBean(new ApiDebugSimpleDTO(), apiDebugMapper.selectByPrimaryKey(anotherAddApiDebug.getId()))); + + // @@校验权限 + requestGetPermissionTest(PermissionConstants.PROJECT_API_DEBUG_READ, DEFAULT_LIST, HTTP_PROTOCOL); + } + + + @Test + @Order(4) + public void get() throws Exception { + // @@请求成功 + MvcResult mvcResult = this.requestGetWithOk(DEFAULT_GET, addApiDebug.getId()) + .andReturn(); + ApiDataUtils.setResolver(MsHTTPElement.class); + ApiDebugDTO apiDebugDTO = ApiDataUtils.parseObject(JSON.toJSONString(parseResponse(mvcResult).get("data")), ApiDebugDTO.class); + // 校验数据是否正确 + ApiDebugDTO copyApiDebugDTO = BeanUtils.copyBean(new ApiDebugDTO(), apiDebugMapper.selectByPrimaryKey(addApiDebug.getId())); + ApiDebugBlob apiDebugBlob = apiDebugBlobMapper.selectByPrimaryKey(addApiDebug.getId()); + copyApiDebugDTO.setRequest(ApiDataUtils.parseObject(new String(apiDebugBlob.getRequest()), AbstractMsTestElement.class)); + Assertions.assertEquals(apiDebugDTO, copyApiDebugDTO); + // @@校验权限 + requestGetPermissionTest(PermissionConstants.PROJECT_API_DEBUG_READ, DEFAULT_GET, apiDebugDTO.getId()); + } + + @Test + @Order(5) + public void delete() throws Exception { + // @@请求成功 + this.requestGetWithOk(DEFAULT_DELETE, addApiDebug.getId()); + // 校验请求成功数据 + Assertions.assertNull(apiDebugMapper.selectByPrimaryKey(addApiDebug.getId())); + Assertions.assertNull(apiDebugBlobMapper.selectByPrimaryKey(addApiDebug.getId())); + // @@校验日志 + checkLog(addApiDebug.getId(), OperationLogType.DELETE); + // @@校验权限 + requestGetPermissionTest(PermissionConstants.PROJECT_API_DEBUG_DELETE, DEFAULT_DELETE, addApiDebug.getId()); + } +} \ No newline at end of file diff --git a/backend/services/api-test/src/test/java/io/metersphere/api/controller/ApiDebugModuleControllerTests.java b/backend/services/api-test/src/test/java/io/metersphere/api/controller/ApiDebugModuleControllerTests.java index 759724f9f3..1268dafc98 100644 --- a/backend/services/api-test/src/test/java/io/metersphere/api/controller/ApiDebugModuleControllerTests.java +++ b/backend/services/api-test/src/test/java/io/metersphere/api/controller/ApiDebugModuleControllerTests.java @@ -273,7 +273,7 @@ public class ApiDebugModuleControllerTests extends BaseTest { request = new ModuleCreateRequest(); request.setProjectId(DEFAULT_PROJECT_ID); request.setName("defaultProject"); - requestPostPermissionTest(PermissionConstants.PROJECT_API_DEBUG_READ_ADD, URL_MODULE_ADD, request); + requestPostPermissionTest(PermissionConstants.PROJECT_API_DEBUG_ADD, URL_MODULE_ADD, request); } @Test @@ -365,7 +365,7 @@ public class ApiDebugModuleControllerTests extends BaseTest { updateRequest = new ModuleUpdateRequest(); updateRequest.setId(apiDebugModules.get(0).getId()); updateRequest.setName("default-update-Project"); - requestPostPermissionTest(PermissionConstants.PROJECT_API_DEBUG_READ_UPDATE, URL_MODULE_UPDATE, updateRequest); + requestPostPermissionTest(PermissionConstants.PROJECT_API_DEBUG_UPDATE, URL_MODULE_UPDATE, updateRequest); } @Test @@ -638,7 +638,7 @@ public class ApiDebugModuleControllerTests extends BaseTest { checkLog(a1Node.getId(), OperationLogType.UPDATE, URL_MODULE_MOVE); checkLog(a3Node.getId(), OperationLogType.UPDATE, URL_MODULE_MOVE); - requestPostPermissionTest(PermissionConstants.PROJECT_API_DEBUG_READ_UPDATE, URL_MODULE_MOVE, request); + requestPostPermissionTest(PermissionConstants.PROJECT_API_DEBUG_UPDATE, URL_MODULE_MOVE, request); } @Test @@ -750,7 +750,7 @@ public class ApiDebugModuleControllerTests extends BaseTest { //service层判断:测试删除空集合 apiDebugModuleService.deleteModule(new ArrayList<>(), "admin", DEFAULT_PROJECT_ID); //校验权限 - requestGetPermissionTest(PermissionConstants.PROJECT_API_DEBUG_READ_DELETE, String.format(URL_MODULE_DELETE, IDGenerator.nextNum())); + requestGetPermissionTest(PermissionConstants.PROJECT_API_DEBUG_DELETE, String.format(URL_MODULE_DELETE, IDGenerator.nextNum())); } diff --git a/backend/services/api-test/src/test/java/io/metersphere/api/controller/MsHTTPElementTest.java b/backend/services/api-test/src/test/java/io/metersphere/api/controller/MsHTTPElementTest.java new file mode 100644 index 0000000000..8680dd8345 --- /dev/null +++ b/backend/services/api-test/src/test/java/io/metersphere/api/controller/MsHTTPElementTest.java @@ -0,0 +1,190 @@ +package io.metersphere.api.controller; + +import io.metersphere.api.util.ApiDataUtils; +import io.metersphere.plugin.api.spi.AbstractMsTestElement; +import io.metersphere.sdk.dto.api.request.http.*; +import io.metersphere.sdk.dto.api.request.http.auth.BasicAuth; +import io.metersphere.sdk.dto.api.request.http.auth.DigestAuth; +import io.metersphere.sdk.dto.api.request.http.auth.HTTPAuth; +import io.metersphere.sdk.dto.api.request.http.auth.NoAuth; +import io.metersphere.sdk.dto.api.request.http.body.*; +import io.metersphere.sdk.dto.api.request.processors.SQLProcessor; +import io.metersphere.sdk.dto.api.request.processors.ScriptProcessor; +import io.metersphere.sdk.dto.api.request.processors.TimeWaitingProcessor; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +import java.util.ArrayList; +import java.util.List; + +/** + * @Author: jianxing + * @CreateTime: 2023-11-07 11:17 + */ +public class MsHTTPElementTest { + public MsHTTPElementTest() { + ApiDataUtils.setResolver(MsHTTPElement.class); + } + + @Test + public void bodyTest() { + + MsHTTPElement msHTTPElement = getMsHttpElement(); + + List bodies = new ArrayList<>(); + + FormDataBody formDataBody = new FormDataBody(); + FormDataKV formDataKV = new FormDataKV(); + formDataKV.setEnable(false); + formDataKV.setContentType("text/plain"); + formDataKV.setEncode(true); + formDataKV.setMaxLength(10); + formDataKV.setMinLength(8); + formDataKV.setParamType("text"); + formDataKV.setDescription("test"); + formDataKV.setRequired(true); + formDataKV.setValue("value"); + formDataKV.setKey("key"); + formDataBody.setFromValues(List.of(formDataKV)); + bodies.add(formDataBody); + + WWWFormBody wwwFormBody = new WWWFormBody(); + wwwFormBody.setFromValues(List.of(formDataKV)); + bodies.add(wwwFormBody); + + JsonBody jsonBody = new JsonBody(); + jsonBody.setValue("{}"); + bodies.add(jsonBody); + + bodies.add(new NoneBody()); + + RawBody rawBody = new RawBody(); + rawBody.setValue("A"); + bodies.add(rawBody); + + XmlBody xmlBody = new XmlBody(); + xmlBody.setValue(""); + bodies.add(xmlBody); + + BinaryBody binaryBody = new BinaryBody(); + binaryBody.setFilePath("/test/a.png"); + binaryBody.setFileName("a.png"); + bodies.add(binaryBody); + + + for (Object body : bodies) { + msHTTPElement.setBody((Body) body); + String json = ApiDataUtils.toJSONString(msHTTPElement); + Assertions.assertNotNull(json); + Assertions.assertEquals(ApiDataUtils.parseObject(json, AbstractMsTestElement.class), msHTTPElement); + } + } + + @Test + public void authConfigTest() { + + MsHTTPElement msHTTPElement = getMsHttpElement(); + + List authConfigs = new ArrayList<>(); + + authConfigs.add(new NoAuth()); + + BasicAuth basicAuth = new BasicAuth(); + basicAuth.setUserName("test"); + basicAuth.setPassword("passwd"); + authConfigs.add(basicAuth); + + DigestAuth digestAuth = new DigestAuth(); + digestAuth.setUserName("test"); + digestAuth.setPassword("passwd"); + authConfigs.add(digestAuth); + + for (Object authConfig : authConfigs) { + msHTTPElement.setAuthConfig((HTTPAuth) authConfig); + String json = ApiDataUtils.toJSONString(msHTTPElement); + Assertions.assertNotNull(json); + Assertions.assertEquals(ApiDataUtils.parseObject(json, AbstractMsTestElement.class), msHTTPElement); + } + } + + @Test + public void msProcessorTest() { + + MsHTTPElement msHTTPElement = getMsHttpElement(); + + List processors = new ArrayList<>(); + + ScriptProcessor scriptProcessor = new ScriptProcessor(); + scriptProcessor.setEnable(true); + scriptProcessor.setScript("script"); + scriptProcessor.setScriptLanguage("js"); + scriptProcessor.setJsrEnable(true); + processors.add(scriptProcessor); + + SQLProcessor sqlProcessor = new SQLProcessor(); + sqlProcessor.setScript("script"); + sqlProcessor.setEnable(true); + sqlProcessor.setDataSourceId("dataSourceId"); + KeyValueParam keyValueParam = new KeyValueParam(); + keyValueParam.setKey("key"); + keyValueParam.setValue("value"); + sqlProcessor.setVariables(List.of(keyValueParam)); + sqlProcessor.setResultVariable("ddd"); + sqlProcessor.setQueryTimeout(1111); + sqlProcessor.setVariableNames("test"); + processors.add(sqlProcessor); + + TimeWaitingProcessor timeWaitingProcessor = new TimeWaitingProcessor(); + timeWaitingProcessor.setDelay(1000); + timeWaitingProcessor.setEnable(true); + processors.add(timeWaitingProcessor); + + msHTTPElement.setPreProcessors(processors); + msHTTPElement.setPostProcessors(processors); + String json = ApiDataUtils.toJSONString(msHTTPElement); + Assertions.assertNotNull(json); + Assertions.assertEquals(ApiDataUtils.parseObject(json, AbstractMsTestElement.class), msHTTPElement); + } + + public static MsHTTPElement getMsHttpElement() { + MsHTTPElement msHTTPElement = new MsHTTPElement(); + msHTTPElement.setUrl("http://www.test.com"); + msHTTPElement.setPath("/test"); + msHTTPElement.setMethod("GET"); + msHTTPElement.setName("name"); + msHTTPElement.setEnable(false); + + Header header = new Header(); + header.setEnable(false); + header.setValue("value"); + header.setKey("key"); + header.setDescription("desc"); + msHTTPElement.setHeaders(List.of(header)); + + RestParam restParam = new RestParam(); + restParam.setKey("key"); + restParam.setValue("value"); + restParam.setEnable(false); + restParam.setDescription("desc"); + restParam.setRequired(true); + msHTTPElement.setRest(List.of(restParam)); + + QueryParam queryParam = new QueryParam(); + queryParam.setKey("key"); + queryParam.setValue("value"); + queryParam.setEnable(false); + queryParam.setDescription("desc"); + queryParam.setRequired(true); + msHTTPElement.setQuery(List.of(queryParam)); + + MsHTTPConfig msHTTPConfig = new MsHTTPConfig(); + msHTTPConfig.setFollowRedirects(true); + msHTTPConfig.setAutoRedirects(true); + msHTTPConfig.setResponseTimeout(1000L); + msHTTPConfig.setConnectTimeout(1000L); + msHTTPConfig.setCertificateAlias("alias"); + msHTTPElement.setOtherConfig(msHTTPConfig); + + return msHTTPElement; + } +} diff --git a/backend/services/api-test/src/test/java/io/metersphere/api/controller/param/ApiDebugAddRequestDefinition.java b/backend/services/api-test/src/test/java/io/metersphere/api/controller/param/ApiDebugAddRequestDefinition.java new file mode 100644 index 0000000000..32188f24e1 --- /dev/null +++ b/backend/services/api-test/src/test/java/io/metersphere/api/controller/param/ApiDebugAddRequestDefinition.java @@ -0,0 +1,46 @@ +package io.metersphere.api.controller.param; + +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.Size; +import lombok.Getter; +import lombok.Setter; + +/** + * @Author: jianxing + * @CreateTime: 2023-11-07 14:49 + */ +@Getter +@Setter +public class ApiDebugAddRequestDefinition { + + @Schema(description = "接口名称", requiredMode = Schema.RequiredMode.REQUIRED) + @NotBlank(message = "{api_debug.name.not_blank}") + @Size(min = 1, max = 255, message = "{api_debug.name.length_range}") + private String name; + + @Schema(description = "接口协议", requiredMode = Schema.RequiredMode.REQUIRED) + @NotBlank(message = "{api_debug.protocol.not_blank}") + @Size(min = 1, max = 20, message = "{api_debug.protocol.length_range}") + private String protocol; + + @Schema(description = "http协议类型post/get/其它协议则是协议名(mqtt)") + private String method; + + @Schema(description = "http协议路径/其它协议则为空") + private String path; + + @Schema(description = "项目fk", requiredMode = Schema.RequiredMode.REQUIRED) + @NotBlank(message = "{api_debug.project_id.not_blank}") + @Size(min = 1, max = 50, message = "{api_debug.project_id.length_range}") + private String projectId; + + @Schema(description = "模块fk", requiredMode = Schema.RequiredMode.REQUIRED) + @NotBlank(message = "{api_debug.module_id.not_blank}") + @Size(min = 1, max = 50, message = "{api_debug.module_id.length_range}") + private String moduleId; + + @Schema(description = "请求内容") + @NotBlank + private String request; +} diff --git a/backend/services/api-test/src/test/java/io/metersphere/api/controller/param/ApiDebugUpdateRequestDefinition.java b/backend/services/api-test/src/test/java/io/metersphere/api/controller/param/ApiDebugUpdateRequestDefinition.java new file mode 100644 index 0000000000..f4d4dba7bb --- /dev/null +++ b/backend/services/api-test/src/test/java/io/metersphere/api/controller/param/ApiDebugUpdateRequestDefinition.java @@ -0,0 +1,41 @@ +package io.metersphere.api.controller.param; + +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.Size; +import lombok.Getter; +import lombok.Setter; + +/** + * @Author: jianxing + * @CreateTime: 2023-11-07 14:33 + */ +@Getter +@Setter +public class ApiDebugUpdateRequestDefinition { + + @Schema(description = "接口pk", requiredMode = Schema.RequiredMode.REQUIRED) + @NotBlank(message = "{api_debug.id.not_blank}") + @Size(min = 1, max = 50, message = "{api_debug.id.length_range}") + private String id; + + @Schema(description = "接口名称", requiredMode = Schema.RequiredMode.REQUIRED) + @NotBlank(message = "{api_debug.name.not_blank}") + @Size(min = 1, max = 255, message = "{api_debug.name.length_range}") + private String name; + + @Schema(description = "http协议类型post/get/其它协议则是协议名(mqtt)") + private String method; + + @Schema(description = "http协议路径/其它协议则为空") + private String path; + + @Schema(description = "模块fk", requiredMode = Schema.RequiredMode.REQUIRED) + @NotBlank(message = "{api_debug.module_id.not_blank}") + @Size(min = 1, max = 50, message = "{api_debug.module_id.length_range}") + private String moduleId; + + @Schema(description = "请求内容") + @NotBlank + private String request; +} diff --git a/backend/services/project-management/src/test/java/io/metersphere/project/controller/ProjectStatusFlowSettingControllerTest.java b/backend/services/project-management/src/test/java/io/metersphere/project/controller/ProjectStatusFlowSettingControllerTest.java index e80d49e09a..1c2cd286da 100644 --- a/backend/services/project-management/src/test/java/io/metersphere/project/controller/ProjectStatusFlowSettingControllerTest.java +++ b/backend/services/project-management/src/test/java/io/metersphere/project/controller/ProjectStatusFlowSettingControllerTest.java @@ -1,10 +1,6 @@ package io.metersphere.project.controller; import io.metersphere.sdk.constants.*; -import io.metersphere.system.dto.sdk.request.StatusDefinitionUpdateRequest; -import io.metersphere.system.dto.sdk.request.StatusFlowUpdateRequest; -import io.metersphere.system.dto.sdk.request.StatusItemAddRequest; -import io.metersphere.system.dto.sdk.request.StatusItemUpdateRequest; import io.metersphere.sdk.util.BeanUtils; import io.metersphere.system.base.BaseTest; import io.metersphere.system.controller.OrganizationStatusFlowSettingControllerTest; @@ -12,8 +8,13 @@ import io.metersphere.system.controller.param.StatusDefinitionUpdateRequestDefin import io.metersphere.system.controller.param.StatusFlowUpdateRequestDefinition; import io.metersphere.system.controller.param.StatusItemAddRequestDefinition; import io.metersphere.system.controller.param.StatusItemUpdateRequestDefinition; -import io.metersphere.system.domain.*; +import io.metersphere.system.domain.OrganizationParameter; +import io.metersphere.system.domain.StatusItem; import io.metersphere.system.dto.StatusItemDTO; +import io.metersphere.system.dto.sdk.request.StatusDefinitionUpdateRequest; +import io.metersphere.system.dto.sdk.request.StatusFlowUpdateRequest; +import io.metersphere.system.dto.sdk.request.StatusItemAddRequest; +import io.metersphere.system.dto.sdk.request.StatusItemUpdateRequest; import io.metersphere.system.log.constants.OperationLogType; import io.metersphere.system.mapper.OrganizationParameterMapper; import io.metersphere.system.mapper.StatusItemMapper; @@ -23,7 +24,6 @@ import io.metersphere.system.service.BaseStatusItemService; import jakarta.annotation.Resource; import org.apache.commons.collections.CollectionUtils; import org.apache.commons.lang3.BooleanUtils; -import org.apache.commons.lang3.StringUtils; import org.junit.jupiter.api.*; import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; import org.springframework.boot.test.context.SpringBootTest; diff --git a/backend/services/system-setting/src/main/java/io/metersphere/system/dto/ProtocolDTO.java b/backend/services/system-setting/src/main/java/io/metersphere/system/dto/ProtocolDTO.java new file mode 100644 index 0000000000..f8b72b6d76 --- /dev/null +++ b/backend/services/system-setting/src/main/java/io/metersphere/system/dto/ProtocolDTO.java @@ -0,0 +1,13 @@ +package io.metersphere.system.dto; + +import lombok.Data; + +/** + * @Author: jianxing + * @CreateTime: 2023-11-07 18:43 + */ +@Data +public class ProtocolDTO { + private String protocol; + private String name; +} diff --git a/backend/services/system-setting/src/main/java/io/metersphere/system/service/ApiPluginService.java b/backend/services/system-setting/src/main/java/io/metersphere/system/service/ApiPluginService.java new file mode 100644 index 0000000000..875fad2372 --- /dev/null +++ b/backend/services/system-setting/src/main/java/io/metersphere/system/service/ApiPluginService.java @@ -0,0 +1,42 @@ +package io.metersphere.system.service; + +import io.metersphere.plugin.api.spi.AbstractProtocolPlugin; +import io.metersphere.sdk.constants.PluginScenarioType; +import io.metersphere.system.domain.Plugin; +import jakarta.annotation.Resource; +import org.pf4j.PluginWrapper; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.List; +import java.util.Set; +import java.util.stream.Collectors; + +@Service +@Transactional(rollbackFor = Exception.class) +public class ApiPluginService { + + @Resource + private PluginLoadService pluginLoadService; + @Resource + private BasePluginService basePluginService; + + /** + * 获取协议插件的的协议列表 + * @param orgId + * @return + */ + public List getProtocols(String orgId) { + // 查询组织下有权限的插件 + Set pluginIds = basePluginService.getOrgEnabledPlugins(orgId, PluginScenarioType.API_PROTOCOL) + .stream() + .map(Plugin::getId) + .collect(Collectors.toSet()); + + List plugins = pluginLoadService.getMsPluginManager().getPlugins(); + return plugins.stream() + .filter(plugin -> pluginIds.contains(plugin.getPluginId()) && plugin.getPlugin() instanceof AbstractProtocolPlugin) + .map(plugin -> ((AbstractProtocolPlugin) plugin.getPlugin()).getProtocol()) + .collect(Collectors.toList()); + } +} diff --git a/backend/services/system-setting/src/main/java/io/metersphere/system/service/BaseUserRoleService.java b/backend/services/system-setting/src/main/java/io/metersphere/system/service/BaseUserRoleService.java index 33a08bc897..48606379b1 100644 --- a/backend/services/system-setting/src/main/java/io/metersphere/system/service/BaseUserRoleService.java +++ b/backend/services/system-setting/src/main/java/io/metersphere/system/service/BaseUserRoleService.java @@ -119,6 +119,7 @@ public class BaseUserRoleService { put("READ+IMPORT", "permission.import"); put("READ+RECOVER", "permission.recover"); put("READ+EXPORT", "permission.export"); + put("READ+EXECUTE", "permission.execute"); }}; return Translator.get(translationMap.get(permissionKey)); } diff --git a/backend/services/system-setting/src/test/java/io/metersphere/system/base/BaseTest.java b/backend/services/system-setting/src/test/java/io/metersphere/system/base/BaseTest.java index aca7c8a58a..4217278c46 100644 --- a/backend/services/system-setting/src/test/java/io/metersphere/system/base/BaseTest.java +++ b/backend/services/system-setting/src/test/java/io/metersphere/system/base/BaseTest.java @@ -264,7 +264,7 @@ public abstract class BaseTest { return JSON.parseArray(JSON.toJSONString(data), clazz); } - private static Map parseResponse(MvcResult mvcResult) throws UnsupportedEncodingException { + protected static Map parseResponse(MvcResult mvcResult) throws UnsupportedEncodingException { return JSON.parseMap(mvcResult.getResponse().getContentAsString(Charset.defaultCharset())); }