From a3e0e5cbe22a6f1e51dd31952477130c42d3f868 Mon Sep 17 00:00:00 2001 From: wxg0103 <727495428@qq.com> Date: Thu, 18 Jan 2024 21:02:15 +0800 Subject: [PATCH] =?UTF-8?q?feat(=E6=8E=A5=E5=8F=A3=E6=B5=8B=E8=AF=95):=20?= =?UTF-8?q?=E6=8E=A5=E5=8F=A3=E7=94=A8=E4=BE=8B=E6=8A=A5=E5=91=8A=E6=8E=A5?= =?UTF-8?q?=E5=8F=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../api/mapper/ApiReportStepMapper.java | 5 +- .../io/metersphere/sdk/domain/ShareInfo.java | 99 ++++++- .../sdk/domain/ShareInfoExample.java | 70 +++++ .../sdk/mapper/ShareInfoMapper.java | 4 + .../sdk/mapper/ShareInfoMapper.xml | 76 ++++- .../migration/3.0.0/ddl/V3.0.0_2__sdk_ddl.sql | 18 +- .../migration/3.0.0/dml/V3.0.0_11_1__data.sql | 54 ++-- .../sdk/constants/PermissionConstants.java | 7 + .../io/metersphere/sdk/util}/ShareUtil.java | 2 +- .../src/main/resources/i18n/api.properties | 2 + .../main/resources/i18n/api_en_US.properties | 5 +- .../main/resources/i18n/api_zh_CN.properties | 2 + .../main/resources/i18n/api_zh_TW.properties | 4 +- .../main/resources/i18n/commons.properties | 1 + .../resources/i18n/commons_en_US.properties | 1 + .../resources/i18n/commons_zh_CN.properties | 1 + .../resources/i18n/commons_zh_TW.properties | 1 + .../api/constants/ShareInfoType.java | 2 +- .../controller/ApiReportShareController.java | 34 +++ .../definition/ApiReportController.java | 79 ++++- .../dto/definition/ApiReportBatchRequest.java | 12 + .../api/dto/definition/ApiReportDTO.java | 15 + .../dto/definition/ApiReportDetailDTO.java | 44 +++ .../dto/definition/ApiReportPageRequest.java | 12 + .../api/mapper/ExtApiReportMapper.java | 8 +- .../api/mapper/ExtApiReportMapper.xml | 38 ++- .../api/service/ApiReportShareService.java | 120 ++++++++ .../api/service/ApiShareService.java | 31 +- .../definition/ApiReportLogService.java | 85 ++++++ .../service/definition/ApiReportService.java | 129 +++++++- .../scenario/ApiScenarioReportService.java | 4 - .../src/main/resources/permission.json | 15 + .../ApiDefinitionScheduleControllerTests.java | 6 +- .../controller/ApiReportControllerTests.java | 277 +++++++++++++++++- .../controller/ApiShareControllerTests.java | 14 +- .../dto/environment/KeyValueEnableParam.java | 4 + .../environment/datasource/DataSource.java | 2 - .../project/service/EnvironmentService.java | 6 +- .../project/service/FileMetadataService.java | 2 +- .../project/service/GlobalParamsService.java | 8 +- .../project/service/ProjectService.java | 8 +- .../EnvironmentControllerTests.java | 32 +- .../EnvironmentGroupControllerTests.java | 6 +- .../GlobalParamsControllerTests.java | 6 +- .../log/constants/OperationLogModule.java | 1 + 45 files changed, 1178 insertions(+), 174 deletions(-) rename backend/{services/workstation/src/main/java/io/metersphere/workstation/utils => framework/sdk/src/main/java/io/metersphere/sdk/util}/ShareUtil.java (97%) create mode 100644 backend/services/api-test/src/main/java/io/metersphere/api/controller/ApiReportShareController.java create mode 100644 backend/services/api-test/src/main/java/io/metersphere/api/dto/definition/ApiReportBatchRequest.java create mode 100644 backend/services/api-test/src/main/java/io/metersphere/api/dto/definition/ApiReportDTO.java create mode 100644 backend/services/api-test/src/main/java/io/metersphere/api/dto/definition/ApiReportDetailDTO.java create mode 100644 backend/services/api-test/src/main/java/io/metersphere/api/dto/definition/ApiReportPageRequest.java create mode 100644 backend/services/api-test/src/main/java/io/metersphere/api/service/ApiReportShareService.java create mode 100644 backend/services/api-test/src/main/java/io/metersphere/api/service/definition/ApiReportLogService.java diff --git a/backend/framework/domain/src/main/java/io/metersphere/api/mapper/ApiReportStepMapper.java b/backend/framework/domain/src/main/java/io/metersphere/api/mapper/ApiReportStepMapper.java index 45f9de2651..fe942a252f 100644 --- a/backend/framework/domain/src/main/java/io/metersphere/api/mapper/ApiReportStepMapper.java +++ b/backend/framework/domain/src/main/java/io/metersphere/api/mapper/ApiReportStepMapper.java @@ -2,9 +2,10 @@ package io.metersphere.api.mapper; import io.metersphere.api.domain.ApiReportStep; import io.metersphere.api.domain.ApiReportStepExample; -import java.util.List; import org.apache.ibatis.annotations.Param; +import java.util.List; + public interface ApiReportStepMapper { long countByExample(ApiReportStepExample example); @@ -30,5 +31,5 @@ public interface ApiReportStepMapper { int batchInsert(@Param("list") List list); - int batchInsertSelective(@Param("list") List list, @Param("selective") ApiReportStep.Column ... selective); + int batchInsertSelective(@Param("list") List list, @Param("selective") ApiReportStep.Column... selective); } \ No newline at end of file diff --git a/backend/framework/domain/src/main/java/io/metersphere/sdk/domain/ShareInfo.java b/backend/framework/domain/src/main/java/io/metersphere/sdk/domain/ShareInfo.java index 3c26f1bf52..bddadf044f 100644 --- a/backend/framework/domain/src/main/java/io/metersphere/sdk/domain/ShareInfo.java +++ b/backend/framework/domain/src/main/java/io/metersphere/sdk/domain/ShareInfo.java @@ -4,32 +4,117 @@ import io.metersphere.validation.groups.*; import io.swagger.v3.oas.annotations.media.Schema; import jakarta.validation.constraints.*; import java.io.Serializable; +import java.util.ArrayList; +import java.util.Arrays; import lombok.Data; @Data public class ShareInfo implements Serializable { - @Schema(description = "分享ID", requiredMode = Schema.RequiredMode.REQUIRED) + @Schema(description = "分享ID", requiredMode = Schema.RequiredMode.REQUIRED) @NotBlank(message = "{share_info.id.not_blank}", groups = {Updated.class}) @Size(min = 1, max = 50, message = "{share_info.id.length_range}", groups = {Created.class, Updated.class}) private String id; - @Schema(description = "创建时间") + @Schema(description = "创建时间") private Long createTime; - @Schema(description = "创建人") + @Schema(description = "创建人") private String createUser; - @Schema(description = "更新时间") + @Schema(description = "更新时间") private Long updateTime; - @Schema(description = "分享类型single batch") + @Schema(description = "分享类型 资源的类型 Single, Batch, API_SHARE_REPORT, TEST_PLAN_SHARE_REPORT") private String shareType; - @Schema(description = "语言") + @Schema(description = "语言") private String lang; - @Schema(description = "分享扩展数据") + @Schema(description = "项目id", requiredMode = Schema.RequiredMode.REQUIRED) + @NotBlank(message = "{share_info.project_id.not_blank}", groups = {Created.class}) + @Size(min = 1, max = 50, message = "{share_info.project_id.length_range}", groups = {Created.class, Updated.class}) + private String projectId; + + @Schema(description = "分享扩展数据 资源的id") private byte[] customData; private static final long serialVersionUID = 1L; + + public enum Column { + id("id", "id", "VARCHAR", false), + createTime("create_time", "createTime", "BIGINT", false), + createUser("create_user", "createUser", "VARCHAR", false), + updateTime("update_time", "updateTime", "BIGINT", false), + shareType("share_type", "shareType", "VARCHAR", false), + lang("lang", "lang", "VARCHAR", false), + projectId("project_id", "projectId", "VARCHAR", false), + customData("custom_data", "customData", "LONGVARBINARY", false); + + private static final String BEGINNING_DELIMITER = "`"; + + private static final String ENDING_DELIMITER = "`"; + + private final String column; + + private final boolean isColumnNameDelimited; + + private final String javaProperty; + + private final String jdbcType; + + public String value() { + return this.column; + } + + public String getValue() { + return this.column; + } + + public String getJavaProperty() { + return this.javaProperty; + } + + public String getJdbcType() { + return this.jdbcType; + } + + Column(String column, String javaProperty, String jdbcType, boolean isColumnNameDelimited) { + this.column = column; + this.javaProperty = javaProperty; + this.jdbcType = jdbcType; + this.isColumnNameDelimited = isColumnNameDelimited; + } + + public String desc() { + return this.getEscapedColumnName() + " DESC"; + } + + public String asc() { + return this.getEscapedColumnName() + " ASC"; + } + + public static Column[] excludes(Column ... excludes) { + ArrayList columns = new ArrayList<>(Arrays.asList(Column.values())); + if (excludes != null && excludes.length > 0) { + columns.removeAll(new ArrayList<>(Arrays.asList(excludes))); + } + return columns.toArray(new Column[]{}); + } + + public static Column[] all() { + return Column.values(); + } + + public String getEscapedColumnName() { + if (this.isColumnNameDelimited) { + return new StringBuilder().append(BEGINNING_DELIMITER).append(this.column).append(ENDING_DELIMITER).toString(); + } else { + return this.column; + } + } + + public String getAliasedEscapedColumnName() { + return this.getEscapedColumnName(); + } + } } \ No newline at end of file diff --git a/backend/framework/domain/src/main/java/io/metersphere/sdk/domain/ShareInfoExample.java b/backend/framework/domain/src/main/java/io/metersphere/sdk/domain/ShareInfoExample.java index b7f2cdef09..f8b563e67c 100644 --- a/backend/framework/domain/src/main/java/io/metersphere/sdk/domain/ShareInfoExample.java +++ b/backend/framework/domain/src/main/java/io/metersphere/sdk/domain/ShareInfoExample.java @@ -503,6 +503,76 @@ public class ShareInfoExample { addCriterion("lang not between", value1, value2, "lang"); return (Criteria) this; } + + public Criteria andProjectIdIsNull() { + addCriterion("project_id is null"); + return (Criteria) this; + } + + public Criteria andProjectIdIsNotNull() { + addCriterion("project_id is not null"); + return (Criteria) this; + } + + public Criteria andProjectIdEqualTo(String value) { + addCriterion("project_id =", value, "projectId"); + return (Criteria) this; + } + + public Criteria andProjectIdNotEqualTo(String value) { + addCriterion("project_id <>", value, "projectId"); + return (Criteria) this; + } + + public Criteria andProjectIdGreaterThan(String value) { + addCriterion("project_id >", value, "projectId"); + return (Criteria) this; + } + + public Criteria andProjectIdGreaterThanOrEqualTo(String value) { + addCriterion("project_id >=", value, "projectId"); + return (Criteria) this; + } + + public Criteria andProjectIdLessThan(String value) { + addCriterion("project_id <", value, "projectId"); + return (Criteria) this; + } + + public Criteria andProjectIdLessThanOrEqualTo(String value) { + addCriterion("project_id <=", value, "projectId"); + return (Criteria) this; + } + + public Criteria andProjectIdLike(String value) { + addCriterion("project_id like", value, "projectId"); + return (Criteria) this; + } + + public Criteria andProjectIdNotLike(String value) { + addCriterion("project_id not like", value, "projectId"); + return (Criteria) this; + } + + public Criteria andProjectIdIn(List values) { + addCriterion("project_id in", values, "projectId"); + return (Criteria) this; + } + + public Criteria andProjectIdNotIn(List values) { + addCriterion("project_id not in", values, "projectId"); + return (Criteria) this; + } + + public Criteria andProjectIdBetween(String value1, String value2) { + addCriterion("project_id between", value1, value2, "projectId"); + return (Criteria) this; + } + + public Criteria andProjectIdNotBetween(String value1, String value2) { + addCriterion("project_id not between", value1, value2, "projectId"); + return (Criteria) this; + } } public static class Criteria extends GeneratedCriteria { diff --git a/backend/framework/domain/src/main/java/io/metersphere/sdk/mapper/ShareInfoMapper.java b/backend/framework/domain/src/main/java/io/metersphere/sdk/mapper/ShareInfoMapper.java index 4662b235bd..9290ba0f2b 100644 --- a/backend/framework/domain/src/main/java/io/metersphere/sdk/mapper/ShareInfoMapper.java +++ b/backend/framework/domain/src/main/java/io/metersphere/sdk/mapper/ShareInfoMapper.java @@ -33,4 +33,8 @@ public interface ShareInfoMapper { int updateByPrimaryKeyWithBLOBs(ShareInfo record); int updateByPrimaryKey(ShareInfo record); + + int batchInsert(@Param("list") List list); + + int batchInsertSelective(@Param("list") List list, @Param("selective") ShareInfo.Column ... selective); } \ No newline at end of file diff --git a/backend/framework/domain/src/main/java/io/metersphere/sdk/mapper/ShareInfoMapper.xml b/backend/framework/domain/src/main/java/io/metersphere/sdk/mapper/ShareInfoMapper.xml index 8c197673cd..27336d6843 100644 --- a/backend/framework/domain/src/main/java/io/metersphere/sdk/mapper/ShareInfoMapper.xml +++ b/backend/framework/domain/src/main/java/io/metersphere/sdk/mapper/ShareInfoMapper.xml @@ -8,6 +8,7 @@ + @@ -71,7 +72,7 @@ - id, create_time, create_user, update_time, share_type, lang + id, create_time, create_user, update_time, share_type, lang, project_id custom_data @@ -127,10 +128,10 @@ insert into share_info (id, create_time, create_user, update_time, share_type, lang, - custom_data) + project_id, custom_data) values (#{id,jdbcType=VARCHAR}, #{createTime,jdbcType=BIGINT}, #{createUser,jdbcType=VARCHAR}, #{updateTime,jdbcType=BIGINT}, #{shareType,jdbcType=VARCHAR}, #{lang,jdbcType=VARCHAR}, - #{customData,jdbcType=LONGVARBINARY}) + #{projectId,jdbcType=VARCHAR}, #{customData,jdbcType=LONGVARBINARY}) insert into share_info @@ -153,6 +154,9 @@ lang, + + project_id, + custom_data, @@ -176,6 +180,9 @@ #{lang,jdbcType=VARCHAR}, + + #{projectId,jdbcType=VARCHAR}, + #{customData,jdbcType=LONGVARBINARY}, @@ -208,6 +215,9 @@ lang = #{record.lang,jdbcType=VARCHAR}, + + project_id = #{record.projectId,jdbcType=VARCHAR}, + custom_data = #{record.customData,jdbcType=LONGVARBINARY}, @@ -224,6 +234,7 @@ update_time = #{record.updateTime,jdbcType=BIGINT}, share_type = #{record.shareType,jdbcType=VARCHAR}, lang = #{record.lang,jdbcType=VARCHAR}, + project_id = #{record.projectId,jdbcType=VARCHAR}, custom_data = #{record.customData,jdbcType=LONGVARBINARY} @@ -236,7 +247,8 @@ create_user = #{record.createUser,jdbcType=VARCHAR}, update_time = #{record.updateTime,jdbcType=BIGINT}, share_type = #{record.shareType,jdbcType=VARCHAR}, - lang = #{record.lang,jdbcType=VARCHAR} + lang = #{record.lang,jdbcType=VARCHAR}, + project_id = #{record.projectId,jdbcType=VARCHAR} @@ -259,6 +271,9 @@ lang = #{lang,jdbcType=VARCHAR}, + + project_id = #{projectId,jdbcType=VARCHAR}, + custom_data = #{customData,jdbcType=LONGVARBINARY}, @@ -272,6 +287,7 @@ update_time = #{updateTime,jdbcType=BIGINT}, share_type = #{shareType,jdbcType=VARCHAR}, lang = #{lang,jdbcType=VARCHAR}, + project_id = #{projectId,jdbcType=VARCHAR}, custom_data = #{customData,jdbcType=LONGVARBINARY} where id = #{id,jdbcType=VARCHAR} @@ -281,7 +297,57 @@ create_user = #{createUser,jdbcType=VARCHAR}, update_time = #{updateTime,jdbcType=BIGINT}, share_type = #{shareType,jdbcType=VARCHAR}, - lang = #{lang,jdbcType=VARCHAR} + lang = #{lang,jdbcType=VARCHAR}, + project_id = #{projectId,jdbcType=VARCHAR} where id = #{id,jdbcType=VARCHAR} + + insert into share_info + (id, create_time, create_user, update_time, share_type, lang, project_id, custom_data + ) + values + + (#{item.id,jdbcType=VARCHAR}, #{item.createTime,jdbcType=BIGINT}, #{item.createUser,jdbcType=VARCHAR}, + #{item.updateTime,jdbcType=BIGINT}, #{item.shareType,jdbcType=VARCHAR}, #{item.lang,jdbcType=VARCHAR}, + #{item.projectId,jdbcType=VARCHAR}, #{item.customData,jdbcType=LONGVARBINARY}) + + + + insert into share_info ( + + ${column.escapedColumnName} + + ) + values + + ( + + + #{item.id,jdbcType=VARCHAR} + + + #{item.createTime,jdbcType=BIGINT} + + + #{item.createUser,jdbcType=VARCHAR} + + + #{item.updateTime,jdbcType=BIGINT} + + + #{item.shareType,jdbcType=VARCHAR} + + + #{item.lang,jdbcType=VARCHAR} + + + #{item.projectId,jdbcType=VARCHAR} + + + #{item.customData,jdbcType=LONGVARBINARY} + + + ) + + \ No newline at end of file diff --git a/backend/framework/domain/src/main/resources/migration/3.0.0/ddl/V3.0.0_2__sdk_ddl.sql b/backend/framework/domain/src/main/resources/migration/3.0.0/ddl/V3.0.0_2__sdk_ddl.sql index bd2e772379..7a208882d9 100644 --- a/backend/framework/domain/src/main/resources/migration/3.0.0/ddl/V3.0.0_2__sdk_ddl.sql +++ b/backend/framework/domain/src/main/resources/migration/3.0.0/ddl/V3.0.0_2__sdk_ddl.sql @@ -156,17 +156,21 @@ CREATE INDEX idx_type ON operation_history (`type`); CREATE TABLE IF NOT EXISTS share_info ( - `id` VARCHAR(50) NOT NULL COMMENT '分享ID' , - `create_time` BIGINT NOT NULL COMMENT '创建时间' , - `create_user` VARCHAR(50) NOT NULL COMMENT '创建人' , - `update_time` BIGINT NOT NULL COMMENT '更新时间' , - `share_type` VARCHAR(64) COMMENT '分享类型single batch' , - `custom_data` LONGBLOB COMMENT '分享扩展数据' , - `lang` VARCHAR(10) COMMENT '语言' , + `id` VARCHAR(50) NOT NULL COMMENT '分享ID' , + `create_time` BIGINT NOT NULL COMMENT '创建时间' , + `create_user` VARCHAR(50) NOT NULL COMMENT '创建人' , + `update_time` BIGINT NOT NULL COMMENT '更新时间' , + `share_type` VARCHAR(64) COMMENT '分享类型 资源的类型 Single, Batch, API_SHARE_REPORT, TEST_PLAN_SHARE_REPORT' , + `custom_data` LONGBLOB COMMENT '分享扩展数据 资源的id' , + `lang` VARCHAR(10) COMMENT '语言' , + `project_id` VARCHAR(50) NOT NULL COMMENT '项目id' , PRIMARY KEY (id) ) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = '分享'; +CREATE INDEX idx_share_type ON share_info(share_type); +CREATE INDEX idx_project_id ON share_info(project_id); + -- set innodb lock wait timeout to default SET SESSION innodb_lock_wait_timeout = DEFAULT; diff --git a/backend/framework/domain/src/main/resources/migration/3.0.0/dml/V3.0.0_11_1__data.sql b/backend/framework/domain/src/main/resources/migration/3.0.0/dml/V3.0.0_11_1__data.sql index e74c2e25cf..d3b613838c 100644 --- a/backend/framework/domain/src/main/resources/migration/3.0.0/dml/V3.0.0_11_1__data.sql +++ b/backend/framework/domain/src/main/resources/migration/3.0.0/dml/V3.0.0_11_1__data.sql @@ -41,10 +41,8 @@ INSERT INTO user_role_permission (id, role_id, permission_id) VALUES (UUID_SHORT INSERT INTO user_role_permission (id, role_id, permission_id) VALUES (UUID_SHORT(), 'member', 'SYSTEM_PERSONAL_API_KEY:READ'); INSERT INTO user_role_permission (id, role_id, permission_id) VALUES (UUID_SHORT(), 'member', 'SYSTEM_PERSONAL:READ'); INSERT INTO user_role_permission (id, role_id, permission_id) VALUES (UUID_SHORT(), 'member', 'SYSTEM_PERSONAL:READ+UPDATE'); -INSERT INTO user_role_permission (id, role_id, permission_id) -VALUES (UUID_SHORT(), 'member', 'PROJECT_TEST_PLAN_MODULE:READ'); -INSERT INTO user_role_permission (id, role_id, permission_id) -VALUES (UUID_SHORT(), 'member', 'PROJECT_TEST_PLAN:READ'); +INSERT INTO user_role_permission (id, role_id, permission_id) VALUES (UUID_SHORT(), 'member', 'PROJECT_TEST_PLAN_MODULE:READ'); +INSERT INTO user_role_permission (id, role_id, permission_id) VALUES (UUID_SHORT(), 'member', 'PROJECT_TEST_PLAN:READ'); -- 组织管理员权限 INSERT INTO user_role_permission (id, role_id, permission_id) VALUES (UUID_SHORT(), 'org_admin', 'ORGANIZATION_USER_ROLE:READ'); @@ -177,6 +175,9 @@ INSERT INTO user_role_permission (id, role_id, permission_id) VALUES (UUID_SHORT INSERT INTO user_role_permission (id, role_id, permission_id) VALUES (UUID_SHORT(), 'project_admin', 'PROJECT_API_SCENARIO:READ+IMPORT'); INSERT INTO user_role_permission (id, role_id, permission_id) VALUES (UUID_SHORT(), 'project_admin', 'PROJECT_API_SCENARIO:READ+EXPORT'); INSERT INTO user_role_permission (id, role_id, permission_id) VALUES (UUID_SHORT(), 'project_admin', 'PROJECT_API_SCENARIO:READ+DEBUG'); +INSERT INTO user_role_permission (id, role_id, permission_id) VALUES (UUID_SHORT(), 'project_admin', 'PROJECT_API_REPORT:READ'); +INSERT INTO user_role_permission (id, role_id, permission_id) VALUES (UUID_SHORT(), 'project_admin', 'PROJECT_API_REPORT:READ+UPDATE'); +INSERT INTO user_role_permission (id, role_id, permission_id) VALUES (UUID_SHORT(), 'project_admin', 'PROJECT_API_REPORT:READ+DELETE'); INSERT INTO user_role_permission (id, role_id, permission_id) VALUES (UUID_SHORT(), 'project_admin', 'FUNCTIONAL_CASE:READ'); INSERT INTO user_role_permission (id, role_id, permission_id) VALUES (UUID_SHORT(), 'project_admin', 'FUNCTIONAL_CASE:READ+ADD'); @@ -192,20 +193,13 @@ INSERT INTO user_role_permission (id, role_id, permission_id) VALUES (UUID_SHORT INSERT INTO user_role_permission (id, role_id, permission_id) VALUES (UUID_SHORT(), 'project_admin', 'CASE_REVIEW:READ+DELETE'); INSERT INTO user_role_permission (id, role_id, permission_id) VALUES (UUID_SHORT(), 'project_admin', 'CASE_REVIEW:READ+REVIEW'); INSERT INTO user_role_permission (id, role_id, permission_id) VALUES (UUID_SHORT(), 'project_admin', 'CASE_REVIEW:READ+RELEVANCE'); -INSERT INTO user_role_permission (id, role_id, permission_id) -VALUES (UUID_SHORT(), 'project_admin', 'PROJECT_TEST_PLAN_MODULE:READ+ADD'); -INSERT INTO user_role_permission (id, role_id, permission_id) -VALUES (UUID_SHORT(), 'project_admin', 'PROJECT_TEST_PLAN_MODULE:READ+UPDATE'); -INSERT INTO user_role_permission (id, role_id, permission_id) -VALUES (UUID_SHORT(), 'project_admin', 'PROJECT_TEST_PLAN_MODULE:READ+DELETE'); -INSERT INTO user_role_permission (id, role_id, permission_id) -VALUES (UUID_SHORT(), 'project_admin', 'PROJECT_TEST_PLAN:READ+ADD'); -INSERT INTO user_role_permission (id, role_id, permission_id) -VALUES (UUID_SHORT(), 'project_admin', 'PROJECT_TEST_PLAN:READ+UPDATE'); -INSERT INTO user_role_permission (id, role_id, permission_id) -VALUES (UUID_SHORT(), 'project_admin', 'PROJECT_TEST_PLAN:READ+DELETE'); -INSERT INTO user_role_permission (id, role_id, permission_id) -VALUES (UUID_SHORT(), 'project_admin', 'PROJECT_TEST_PLAN:READ+EXECUTE'); +INSERT INTO user_role_permission (id, role_id, permission_id) VALUES (UUID_SHORT(), 'project_admin', 'PROJECT_TEST_PLAN_MODULE:READ+ADD'); +INSERT INTO user_role_permission (id, role_id, permission_id) VALUES (UUID_SHORT(), 'project_admin', 'PROJECT_TEST_PLAN_MODULE:READ+UPDATE'); +INSERT INTO user_role_permission (id, role_id, permission_id) VALUES (UUID_SHORT(), 'project_admin', 'PROJECT_TEST_PLAN_MODULE:READ+DELETE'); +INSERT INTO user_role_permission (id, role_id, permission_id) VALUES (UUID_SHORT(), 'project_admin', 'PROJECT_TEST_PLAN:READ+ADD'); +INSERT INTO user_role_permission (id, role_id, permission_id) VALUES (UUID_SHORT(), 'project_admin', 'PROJECT_TEST_PLAN:READ+UPDATE'); +INSERT INTO user_role_permission (id, role_id, permission_id) VALUES (UUID_SHORT(), 'project_admin', 'PROJECT_TEST_PLAN:READ+DELETE'); +INSERT INTO user_role_permission (id, role_id, permission_id) VALUES (UUID_SHORT(), 'project_admin', 'PROJECT_TEST_PLAN:READ+EXECUTE'); -- 项目成员权限 INSERT INTO user_role_permission (id, role_id, permission_id) VALUES (UUID_SHORT(), 'project_member', 'PROJECT_BASE_INFO:READ'); @@ -296,6 +290,9 @@ INSERT INTO user_role_permission (id, role_id, permission_id) VALUES (UUID_SHORT INSERT INTO user_role_permission (id, role_id, permission_id) VALUES (UUID_SHORT(), 'project_member', 'PROJECT_API_SCENARIO:READ+IMPORT'); INSERT INTO user_role_permission (id, role_id, permission_id) VALUES (UUID_SHORT(), 'project_member', 'PROJECT_API_SCENARIO:READ+EXPORT'); INSERT INTO user_role_permission (id, role_id, permission_id) VALUES (UUID_SHORT(), 'project_member', 'PROJECT_API_SCENARIO:READ+DEBUG'); +INSERT INTO user_role_permission (id, role_id, permission_id) VALUES (UUID_SHORT(), 'project_member', 'PROJECT_API_REPORT:READ'); +INSERT INTO user_role_permission (id, role_id, permission_id) VALUES (UUID_SHORT(), 'project_member', 'PROJECT_API_REPORT:READ+UPDATE'); +INSERT INTO user_role_permission (id, role_id, permission_id) VALUES (UUID_SHORT(), 'project_member', 'PROJECT_API_REPORT:READ+DELETE'); INSERT INTO user_role_permission (id, role_id, permission_id) VALUES (UUID_SHORT(), 'project_member', 'FUNCTIONAL_CASE:READ'); INSERT INTO user_role_permission (id, role_id, permission_id) VALUES (UUID_SHORT(), 'project_member', 'FUNCTIONAL_CASE:READ+ADD'); @@ -311,20 +308,13 @@ INSERT INTO user_role_permission (id, role_id, permission_id) VALUES (UUID_SHORT INSERT INTO user_role_permission (id, role_id, permission_id) VALUES (UUID_SHORT(), 'project_member', 'CASE_REVIEW:READ+DELETE'); INSERT INTO user_role_permission (id, role_id, permission_id) VALUES (UUID_SHORT(), 'project_member', 'CASE_REVIEW:READ+REVIEW'); INSERT INTO user_role_permission (id, role_id, permission_id) VALUES (UUID_SHORT(), 'project_member', 'CASE_REVIEW:READ+RELEVANCE'); -INSERT INTO user_role_permission (id, role_id, permission_id) -VALUES (UUID_SHORT(), 'project_member', 'PROJECT_TEST_PLAN_MODULE:READ+ADD'); -INSERT INTO user_role_permission (id, role_id, permission_id) -VALUES (UUID_SHORT(), 'project_member', 'PROJECT_TEST_PLAN_MODULE:READ+UPDATE'); -INSERT INTO user_role_permission (id, role_id, permission_id) -VALUES (UUID_SHORT(), 'project_member', 'PROJECT_TEST_PLAN_MODULE:READ+DELETE'); -INSERT INTO user_role_permission (id, role_id, permission_id) -VALUES (UUID_SHORT(), 'project_member', 'PROJECT_TEST_PLAN:READ+ADD'); -INSERT INTO user_role_permission (id, role_id, permission_id) -VALUES (UUID_SHORT(), 'project_member', 'PROJECT_TEST_PLAN:READ+UPDATE'); -INSERT INTO user_role_permission (id, role_id, permission_id) -VALUES (UUID_SHORT(), 'project_member', 'PROJECT_TEST_PLAN:READ+DELETE'); -INSERT INTO user_role_permission (id, role_id, permission_id) -VALUES (UUID_SHORT(), 'project_member', 'PROJECT_TEST_PLAN:READ+EXECUTE'); +INSERT INTO user_role_permission (id, role_id, permission_id) VALUES (UUID_SHORT(), 'project_member', 'PROJECT_TEST_PLAN_MODULE:READ+ADD'); +INSERT INTO user_role_permission (id, role_id, permission_id) VALUES (UUID_SHORT(), 'project_member', 'PROJECT_TEST_PLAN_MODULE:READ+UPDATE'); +INSERT INTO user_role_permission (id, role_id, permission_id) VALUES (UUID_SHORT(), 'project_member', 'PROJECT_TEST_PLAN_MODULE:READ+DELETE'); +INSERT INTO user_role_permission (id, role_id, permission_id) VALUES (UUID_SHORT(), 'project_member', 'PROJECT_TEST_PLAN:READ+ADD'); +INSERT INTO user_role_permission (id, role_id, permission_id) VALUES (UUID_SHORT(), 'project_member', 'PROJECT_TEST_PLAN:READ+UPDATE'); +INSERT INTO user_role_permission (id, role_id, permission_id) VALUES (UUID_SHORT(), 'project_member', 'PROJECT_TEST_PLAN:READ+DELETE'); +INSERT INTO user_role_permission (id, role_id, permission_id) VALUES (UUID_SHORT(), 'project_member', 'PROJECT_TEST_PLAN:READ+EXECUTE'); -- 初始化当前站点配置 INSERT into system_parameter values('base.url', 'http://127.0.0.1:8081', 'text'); -- 初始化prometheus站点配置 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 ab056f4a73..46f1eac0da 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 @@ -286,6 +286,13 @@ public class PermissionConstants { /*------ end: API_MANAGEMENT ------*/ + + /*------ start: API_REPORT ------*/ + public static final String PROJECT_API_REPORT_READ = "PROJECT_API_REPORT:READ"; + public static final String PROJECT_API_REPORT_UPDATE = "PROJECT_API_REPORT:READ+UPDATE"; + public static final String PROJECT_API_REPORT_DELETE = "PROJECT_API_REPORT:READ+DELETE"; + /*------ end: API_REPORT ------*/ + //个人中心 /*------ start: PERSONAL_CENTER ------*/ public static final String SYSTEM_PERSONAL_API_KEY_ADD = "SYSTEM_PERSONAL_API_KEY:READ+ADD"; diff --git a/backend/services/workstation/src/main/java/io/metersphere/workstation/utils/ShareUtil.java b/backend/framework/sdk/src/main/java/io/metersphere/sdk/util/ShareUtil.java similarity index 97% rename from backend/services/workstation/src/main/java/io/metersphere/workstation/utils/ShareUtil.java rename to backend/framework/sdk/src/main/java/io/metersphere/sdk/util/ShareUtil.java index 55246c7524..062b6e501e 100644 --- a/backend/services/workstation/src/main/java/io/metersphere/workstation/utils/ShareUtil.java +++ b/backend/framework/sdk/src/main/java/io/metersphere/sdk/util/ShareUtil.java @@ -1,4 +1,4 @@ -package io.metersphere.workstation.utils; +package io.metersphere.sdk.util; import org.apache.commons.lang3.StringUtils; diff --git a/backend/framework/sdk/src/main/resources/i18n/api.properties b/backend/framework/sdk/src/main/resources/i18n/api.properties index c169283546..b3c6b0ad40 100644 --- a/backend/framework/sdk/src/main/resources/i18n/api.properties +++ b/backend/framework/sdk/src/main/resources/i18n/api.properties @@ -317,4 +317,6 @@ resource_pool_execute_error=资源池调用失败 api_swagger_url_error=Swagger url无法连通 api_scenario_exist=场景已存在 schedule_not_exist=定时任务不存在 +api_case_report_not_exist=用例报告不存在 +api_scenario_report_not_exist=场景报告不存在 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 609ade806e..f2bf6713ae 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 @@ -49,7 +49,6 @@ api_scenario_step.id.length_range=Step ID length must be between 1-50 api_scenario_step.scenario_id.not_blank=Scene ID cannot be empty api_scenario_step.scenario_id.length_range=Scene ID length must be between 1-50 api_scenario_step.sort.not_blank=SORT cannot be empty - #module:ApiTestCaseFollow api_test_case_follow.case_id.length_range=Use case fk length must be between 1-50 api_test_case_follow.case_id.not_blank=Use case fk cannot be empty @@ -321,4 +320,6 @@ execute_resource_pool_not_config_error=Select a resource pool in 【Project Mana resource_pool_execute_error=The resource pool call failed api_swagger_url_error=Swagger url unable to connect api_scenario_exist=The scenario already exists -schedule_not_exist=The scheduled task does not exist \ No newline at end of file +schedule_not_exist=The scheduled task does not exist +api_case_report_not_exist=Api report does not exist +api_scenario_report_not_exist=Scenario report does not exist \ 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 b3bcf8203f..4c4b7bbeee 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 @@ -321,3 +321,5 @@ resource_pool_execute_error=资源池调用失败 api_swagger_url_error=Swagger url无法连通 api_scenario_exist=场景已存在 schedule_not_exist=定时任务不存在 +api_case_report_not_exist=用例报告不存在 +api_scenario_report_not_exist=场景报告不存在 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 5f127022a5..96b9d0f519 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 @@ -320,4 +320,6 @@ execute_resource_pool_not_config_error=請在【項目管理-應用管理-接口 resource_pool_execute_error=資源池調用失敗 api_swagger_url_error=Swagger url無法調解 api_scenario_exist=場景已存在 -schedule_not_exist=定時任務不存在 \ No newline at end of file +schedule_not_exist=定時任務不存在 +api_case_report_not_exist=用例報告不存在 +api_scenario_report_not_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 7f38593c20..5e24b44bcf 100644 --- a/backend/framework/sdk/src/main/resources/i18n/commons.properties +++ b/backend/framework/sdk/src/main/resources/i18n/commons.properties @@ -484,6 +484,7 @@ permission.api_mock.name=接口Mock permission.api_definition.import=导入 permission.api_definition.export=导出 permission.api_definition.execute=执行 +permission.api_report=接口报告 #接口管理 api_definition_not_exist=接口不存在 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 32ce2416b1..8aebfae9be 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 @@ -494,6 +494,7 @@ permission.api_mock.name=API Mock permission.api_definition.import=Import permission.api_definition.export=Export permission.api_definition.execute=Execute +permission.api_report=Api Report #接口管理 api_definition_not_exist=API definition is not exist 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 be8e63798c..fe3c46da44 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 @@ -490,6 +490,7 @@ permission.api_mock.name=接口Mock permission.api_definition.import=导入 permission.api_definition.export=导出 permission.api_definition.execute=执行 +permission.api_report=接口报告 #接口管理 api_definition_not_exist=接口不存在 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 55e0282c0e..6001c3c619 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 @@ -490,6 +490,7 @@ permission.api_mock.name=接口Mock permission.api_definition.import=導入 permission.api_definition.export=導出 permission.api_definition.execute=執行 +permission.api_report=接口報告 #接口管理 api_definition_not_exist=接口不存在 diff --git a/backend/services/api-test/src/main/java/io/metersphere/api/constants/ShareInfoType.java b/backend/services/api-test/src/main/java/io/metersphere/api/constants/ShareInfoType.java index 7a19e55d87..8cf24c2f8d 100644 --- a/backend/services/api-test/src/main/java/io/metersphere/api/constants/ShareInfoType.java +++ b/backend/services/api-test/src/main/java/io/metersphere/api/constants/ShareInfoType.java @@ -6,5 +6,5 @@ package io.metersphere.api.constants; * @version: 1.0 */ public enum ShareInfoType { - Single, Batch + SINGLE, BATCH, API_SHARE_REPORT, TEST_PLAN_SHARE_REPORT } diff --git a/backend/services/api-test/src/main/java/io/metersphere/api/controller/ApiReportShareController.java b/backend/services/api-test/src/main/java/io/metersphere/api/controller/ApiReportShareController.java new file mode 100644 index 0000000000..e24886dde0 --- /dev/null +++ b/backend/services/api-test/src/main/java/io/metersphere/api/controller/ApiReportShareController.java @@ -0,0 +1,34 @@ +package io.metersphere.api.controller; + +import io.metersphere.api.dto.share.ShareInfoDTO; +import io.metersphere.api.service.ApiReportShareService; +import io.metersphere.sdk.domain.ShareInfo; +import io.metersphere.system.utils.SessionUtils; +import io.metersphere.validation.groups.Created; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.annotation.Resource; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import java.util.Objects; + +@RestController +@RequestMapping(value = "/api/report/share") +@Tag(name = "接口测试-接口报告-分享") +public class ApiReportShareController { + + @Resource + private ApiReportShareService apiReportShareService; + + @PostMapping("/gen") + public ShareInfoDTO generateShareInfo(@Validated(Created.class) @RequestBody ShareInfo request) { + return apiReportShareService.gen(request, Objects.requireNonNull(SessionUtils.getUser())); + } + + @GetMapping("/get/{id}") + public ShareInfo get(@PathVariable String id) { + return apiReportShareService.get(id); + } + + +} diff --git a/backend/services/api-test/src/main/java/io/metersphere/api/controller/definition/ApiReportController.java b/backend/services/api-test/src/main/java/io/metersphere/api/controller/definition/ApiReportController.java index 3ba4cdf485..cbd993afb0 100644 --- a/backend/services/api-test/src/main/java/io/metersphere/api/controller/definition/ApiReportController.java +++ b/backend/services/api-test/src/main/java/io/metersphere/api/controller/definition/ApiReportController.java @@ -3,9 +3,14 @@ package io.metersphere.api.controller.definition; import com.github.pagehelper.Page; import com.github.pagehelper.PageHelper; import io.metersphere.api.domain.ApiReport; +import io.metersphere.api.dto.definition.ApiReportBatchRequest; +import io.metersphere.api.dto.definition.ApiReportDTO; +import io.metersphere.api.dto.definition.ApiReportDetailDTO; +import io.metersphere.api.dto.definition.ApiReportPageRequest; +import io.metersphere.api.service.ApiReportShareService; import io.metersphere.api.service.definition.ApiReportService; import io.metersphere.sdk.constants.PermissionConstants; -import io.metersphere.system.dto.sdk.BasePageRequest; +import io.metersphere.sdk.domain.ShareInfo; import io.metersphere.system.security.CheckOwner; import io.metersphere.system.utils.PageUtils; import io.metersphere.system.utils.Pager; @@ -14,12 +19,10 @@ import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.tags.Tag; import jakarta.annotation.Resource; import org.apache.commons.lang3.StringUtils; +import org.apache.shiro.authz.annotation.Logical; import org.apache.shiro.authz.annotation.RequiresPermissions; import org.springframework.validation.annotation.Validated; -import org.springframework.web.bind.annotation.PostMapping; -import org.springframework.web.bind.annotation.RequestBody; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.bind.annotation.*; import java.util.List; @@ -29,15 +32,75 @@ import java.util.List; public class ApiReportController { @Resource private ApiReportService apiReportService; + @Resource + private ApiReportShareService apiReportShareService; @PostMapping("/page") @Operation(summary = "接口测试-接口报告-用例()") - @RequiresPermissions(PermissionConstants.PROJECT_API_SCENARIO_READ) @CheckOwner(resourceId = "#request.getProjectId()", resourceType = "project") - public Pager> getPage(@Validated @RequestBody BasePageRequest request) { + @RequiresPermissions(PermissionConstants.PROJECT_API_REPORT_READ) + public Pager> getPage(@Validated @RequestBody ApiReportPageRequest request) { Page page = PageHelper.startPage(request.getCurrent(), request.getPageSize(), StringUtils.isNotBlank(request.getSortString()) ? request.getSortString() : "start_time desc"); - return PageUtils.setPageInfo(page, apiReportService.getPage(request, SessionUtils.getCurrentProjectId())); + return PageUtils.setPageInfo(page, apiReportService.getPage(request)); + } + + @GetMapping("/rename/{id}/{name}") + @Operation(summary = "接口测试-接口报告-用例报告重命名") + @CheckOwner(resourceId = "#id", resourceType = "api_report") + @RequiresPermissions(PermissionConstants.PROJECT_API_REPORT_UPDATE) + public void rename(@PathVariable String id, @PathVariable String name) { + apiReportService.rename(id, name, SessionUtils.getUserId()); + } + + @GetMapping("/delete/{id}") + @Operation(summary = "接口测试-接口报告-用例报告删除") + @CheckOwner(resourceId = "#id", resourceType = "api_report") + @RequiresPermissions(PermissionConstants.PROJECT_API_REPORT_DELETE) + public void delete(@PathVariable String id) { + apiReportService.delete(id, SessionUtils.getUserId()); + } + + @PostMapping("/batch/delete") + @Operation(summary = "接口测试-接口报告-用例报告批量删除") + @CheckOwner(resourceId = "#request.getSelectIds()", resourceType = "api_report") + @RequiresPermissions(PermissionConstants.PROJECT_API_REPORT_DELETE) + public void batchDelete(@RequestBody ApiReportBatchRequest request) { + apiReportService.batchDelete(request, SessionUtils.getUserId()); + } + + @GetMapping("/get/{id}") + @Operation(summary = "接口测试-接口报告-报告获取") + @CheckOwner(resourceId = "#id", resourceType = "api_report") + @RequiresPermissions(value = {PermissionConstants.PROJECT_API_REPORT_READ, PermissionConstants.PROJECT_API_DEFINITION_CASE_UPDATE}, logical = Logical.OR) + public ApiReportDTO get(@PathVariable String id) { + return apiReportService.get(id); + } + + @GetMapping("/get/{shareId}/{reportId}") + @Operation(summary = "接口测试-接口报告-分享报告获取") + public ApiReport get(@PathVariable String shareId, @PathVariable String reportId) { + ShareInfo shareInfo = apiReportShareService.checkResource(shareId); + apiReportShareService.validateExpired(shareInfo); + return apiReportService.get(reportId); + } + + @GetMapping("/get/detail/{reportId}/{stepId}") + @Operation(summary = "接口测试-接口报告-报告详情获取") + @CheckOwner(resourceId = "#reportId", resourceType = "api_report") + @RequiresPermissions(PermissionConstants.PROJECT_API_REPORT_READ) + public List getDetail(@PathVariable String stepId, + @PathVariable String reportId) { + return apiReportService.getDetail(stepId, reportId); + } + + @GetMapping("/get/detail/{shareId}/{reportId}/{stepId}") + public List selectReportContent(@PathVariable String shareId, + @PathVariable String reportId, + @PathVariable String stepId) { + ShareInfo shareInfo = apiReportShareService.checkResource(shareId); + apiReportShareService.validateExpired(shareInfo); + return apiReportService.getDetail(stepId, reportId); } } diff --git a/backend/services/api-test/src/main/java/io/metersphere/api/dto/definition/ApiReportBatchRequest.java b/backend/services/api-test/src/main/java/io/metersphere/api/dto/definition/ApiReportBatchRequest.java new file mode 100644 index 0000000000..ce021e0d79 --- /dev/null +++ b/backend/services/api-test/src/main/java/io/metersphere/api/dto/definition/ApiReportBatchRequest.java @@ -0,0 +1,12 @@ +package io.metersphere.api.dto.definition; + +import io.metersphere.system.dto.table.TableBatchProcessDTO; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +@Data +public class ApiReportBatchRequest extends TableBatchProcessDTO { + @Schema(description = "项目id", requiredMode = Schema.RequiredMode.REQUIRED) + private String projectId; + +} diff --git a/backend/services/api-test/src/main/java/io/metersphere/api/dto/definition/ApiReportDTO.java b/backend/services/api-test/src/main/java/io/metersphere/api/dto/definition/ApiReportDTO.java new file mode 100644 index 0000000000..fc25e8532a --- /dev/null +++ b/backend/services/api-test/src/main/java/io/metersphere/api/dto/definition/ApiReportDTO.java @@ -0,0 +1,15 @@ +package io.metersphere.api.dto.definition; + +import io.metersphere.api.domain.ApiReport; +import io.metersphere.api.domain.ApiReportStep; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.util.List; + +@Data +public class ApiReportDTO extends ApiReport { + @Schema(description = "子节点") + private List children; + +} diff --git a/backend/services/api-test/src/main/java/io/metersphere/api/dto/definition/ApiReportDetailDTO.java b/backend/services/api-test/src/main/java/io/metersphere/api/dto/definition/ApiReportDetailDTO.java new file mode 100644 index 0000000000..6ccecac240 --- /dev/null +++ b/backend/services/api-test/src/main/java/io/metersphere/api/dto/definition/ApiReportDetailDTO.java @@ -0,0 +1,44 @@ +package io.metersphere.api.dto.definition; + +import io.metersphere.sdk.dto.api.result.RequestResult; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +@Data +public class ApiReportDetailDTO { + + @Schema(description = "报告详情id") + private String id; + + @Schema(description = "接口报告id") + private String reportId; + + @Schema(description = "各个步骤请求唯一标识") + private String stepId; + + @Schema(description = "结果状态") + private String status; + + @Schema(description = "误报编号/误报状态独有") + private String fakeCode; + + @Schema(description = "请求名称") + private String requestName; + + @Schema(description = "请求耗时") + private Long requestTime; + + @Schema(description = "请求响应码") + private String code; + + @Schema(description = "响应内容大小") + private Long responseSize; + + @Schema(description = "脚本标识") + private String scriptIdentifier; + + @Schema(description = "结果内容详情") + private RequestResult content; + + +} diff --git a/backend/services/api-test/src/main/java/io/metersphere/api/dto/definition/ApiReportPageRequest.java b/backend/services/api-test/src/main/java/io/metersphere/api/dto/definition/ApiReportPageRequest.java new file mode 100644 index 0000000000..06304e3b12 --- /dev/null +++ b/backend/services/api-test/src/main/java/io/metersphere/api/dto/definition/ApiReportPageRequest.java @@ -0,0 +1,12 @@ +package io.metersphere.api.dto.definition; + +import io.metersphere.system.dto.sdk.BasePageRequest; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +@Data +public class ApiReportPageRequest extends BasePageRequest { + @Schema(description = "项目id", requiredMode = Schema.RequiredMode.REQUIRED) + private String projectId; + +} diff --git a/backend/services/api-test/src/main/java/io/metersphere/api/mapper/ExtApiReportMapper.java b/backend/services/api-test/src/main/java/io/metersphere/api/mapper/ExtApiReportMapper.java index 237c609c91..26a6ca742a 100644 --- a/backend/services/api-test/src/main/java/io/metersphere/api/mapper/ExtApiReportMapper.java +++ b/backend/services/api-test/src/main/java/io/metersphere/api/mapper/ExtApiReportMapper.java @@ -1,12 +1,16 @@ package io.metersphere.api.mapper; import io.metersphere.api.domain.ApiReport; -import io.metersphere.system.dto.sdk.BasePageRequest; +import io.metersphere.api.dto.definition.ApiReportBatchRequest; +import io.metersphere.api.dto.definition.ApiReportPageRequest; import org.apache.ibatis.annotations.Param; import java.util.List; public interface ExtApiReportMapper { - List list(@Param("request") BasePageRequest request, @Param("projectId") String projectId); + List list(@Param("request") ApiReportPageRequest request); + List getIds(@Param("request") ApiReportBatchRequest request); + + List selectApiReportByIds(@Param("ids") List ids, @Param("deleted") boolean deleted); } diff --git a/backend/services/api-test/src/main/java/io/metersphere/api/mapper/ExtApiReportMapper.xml b/backend/services/api-test/src/main/java/io/metersphere/api/mapper/ExtApiReportMapper.xml index 87e6b1fb2b..58edf3e359 100644 --- a/backend/services/api-test/src/main/java/io/metersphere/api/mapper/ExtApiReportMapper.xml +++ b/backend/services/api-test/src/main/java/io/metersphere/api/mapper/ExtApiReportMapper.xml @@ -13,16 +13,48 @@ api_report.create_user, api_report.update_user, api_report.trigger_mode - from api_report where api_report.project_id = #{projectId} + from api_report where api_report.deleted = false + and api_report.test_plan_id = 'NONE' and ( api_report.name like concat('%', #{request.keyword},'%') ) + + and api_report.project_id = #{request.projectId} + + + @@ -38,6 +70,10 @@ and api_report.status in + + and api_report.trigger_mode in + + diff --git a/backend/services/api-test/src/main/java/io/metersphere/api/service/ApiReportShareService.java b/backend/services/api-test/src/main/java/io/metersphere/api/service/ApiReportShareService.java new file mode 100644 index 0000000000..9b5486f4ab --- /dev/null +++ b/backend/services/api-test/src/main/java/io/metersphere/api/service/ApiReportShareService.java @@ -0,0 +1,120 @@ +package io.metersphere.api.service; + +import io.metersphere.api.constants.ShareInfoType; +import io.metersphere.api.domain.ApiReport; +import io.metersphere.api.dto.share.ShareInfoDTO; +import io.metersphere.api.mapper.ApiReportMapper; +import io.metersphere.api.mapper.ApiScenarioReportMapper; +import io.metersphere.project.domain.ProjectApplication; +import io.metersphere.project.domain.ProjectApplicationExample; +import io.metersphere.project.mapper.ProjectApplicationMapper; +import io.metersphere.sdk.domain.ShareInfo; +import io.metersphere.sdk.exception.MSException; +import io.metersphere.sdk.mapper.ShareInfoMapper; +import io.metersphere.sdk.util.Translator; +import io.metersphere.system.dto.sdk.SessionUser; +import io.metersphere.system.uid.IDGenerator; +import jakarta.annotation.Resource; +import org.apache.commons.collections.CollectionUtils; +import org.apache.commons.lang3.BooleanUtils; +import org.apache.commons.lang3.StringUtils; +import org.springframework.context.i18n.LocaleContextHolder; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Propagation; +import org.springframework.transaction.annotation.Transactional; + +import java.util.List; + +import static io.metersphere.sdk.util.ShareUtil.getTimeMills; + +@Service +@Transactional(rollbackFor = Exception.class) +public class ApiReportShareService { + @Resource + private ApiReportMapper apiReportMapper; + @Resource + private ShareInfoMapper shareInfoMapper; + @Resource + private ProjectApplicationMapper projectApplicationMapper; + @Resource + private ApiScenarioReportMapper apiScenarioReportMapper; + + private static final Long DEFAULT = 1000L * 60 * 60 * 24 * 30; + + @Transactional(propagation = Propagation.NOT_SUPPORTED) + public void validateExpired(ShareInfo shareInfo) { + String shareType = shareInfo.getShareType(); + String projectId = ""; + if (StringUtils.equals(shareType, ShareInfoType.API_SHARE_REPORT.name())) { + ApiReport apiReport = apiReportMapper.selectByPrimaryKey(new String(shareInfo.getCustomData())); + if (apiReport != null && BooleanUtils.isFalse(apiReport.getDeleted())) { + projectId = apiReport.getProjectId(); + } /*else { + ApiScenarioReport result = apiScenarioReportMapper.selectByPrimaryKey(new String(shareInfo.getCustomData())); + if (result != null && BooleanUtils.isFalse(result.getDeleted())) { + projectId = result.getProjectId(); + } + }*/ + } + if (StringUtils.isBlank(projectId)) { + throw new MSException(Translator.get("api_case_report_not_exist")); + } + ProjectApplicationExample example = new ProjectApplicationExample(); + example.createCriteria().andProjectIdEqualTo(projectId).andTypeEqualTo(shareType); + List projectApplications = projectApplicationMapper.selectByExample(example); + if (CollectionUtils.isEmpty(projectApplications)) { + millisCheck(System.currentTimeMillis() - shareInfo.getUpdateTime(), DEFAULT, shareInfo.getId()); + } else { + String expr = projectApplications.getFirst().getTypeValue(); + long timeMills = getTimeMills(shareInfo.getUpdateTime(), expr); + millisCheck(System.currentTimeMillis(), timeMills, shareInfo.getId()); + } + + } + + private void millisCheck(long compareMillis, long millis, String shareInfoId) { + if (compareMillis > millis) { + shareInfoMapper.deleteByPrimaryKey(shareInfoId); + throw new MSException(Translator.get("connection_expired")); + } + } + + public ShareInfo createShareInfo(ShareInfo shareInfo) { + long createTime = System.currentTimeMillis(); + shareInfo.setId(IDGenerator.nextStr()); + shareInfo.setCreateTime(createTime); + shareInfo.setUpdateTime(createTime); + shareInfoMapper.insert(shareInfo); + return shareInfo; + } + + public ShareInfoDTO conversionShareInfoToDTO(ShareInfo shareInfo) { + ShareInfoDTO returnDTO = new ShareInfoDTO(); + if (null != shareInfo.getCustomData()) { + String url = "?shareId=" + shareInfo.getId(); + returnDTO.setId(shareInfo.getId()); + returnDTO.setShareUrl(url); + } + return returnDTO; + } + + public ShareInfoDTO gen(ShareInfo request, SessionUser user) { + String lang = user.getLanguage() == null ? LocaleContextHolder.getLocale().toString() : user.getLanguage(); + request.setLang(lang); + request.setCreateUser(user.getId()); + ShareInfo shareInfo = createShareInfo(request); + return conversionShareInfoToDTO(shareInfo); + } + + public ShareInfo checkResource(String id) { + ShareInfo shareInfo = shareInfoMapper.selectByPrimaryKey(id); + if (shareInfo == null) { + throw new RuntimeException(Translator.get("connection_expired")); + } + return shareInfo; + } + + public ShareInfo get(String id) { + return checkResource(id); + } +} diff --git a/backend/services/api-test/src/main/java/io/metersphere/api/service/ApiShareService.java b/backend/services/api-test/src/main/java/io/metersphere/api/service/ApiShareService.java index ab95a95d62..e11f7e70b8 100644 --- a/backend/services/api-test/src/main/java/io/metersphere/api/service/ApiShareService.java +++ b/backend/services/api-test/src/main/java/io/metersphere/api/service/ApiShareService.java @@ -19,7 +19,6 @@ import org.springframework.stereotype.Service; import java.util.List; import java.util.Optional; -import java.util.UUID; /** * @author: LAN @@ -37,6 +36,8 @@ public class ApiShareService { @Resource ApiDefinitionService apiDefinitionService; + @Resource + ApiReportShareService apiReportShareService; /** * 生成 api 接口文档分享信息 @@ -54,30 +55,30 @@ public class ApiShareService { shareInfoRequest.setCreateUser(user.getId()); shareInfoRequest.setLang(lang); shareInfoRequest.setCustomData(data.getBytes()); + shareInfoRequest.setProjectId(request.getProjectId()); }); ShareInfo shareInfo = Optional.ofNullable(customData) .map(data -> genShareInfo(shareInfoRequest)) .orElse(new ShareInfo()); - return conversionShareInfoToDTO(shareInfo); + return apiReportShareService.conversionShareInfoToDTO(shareInfo); } private String genCustomData(ApiDefinitionDocRequest request, ShareInfo shareInfoRequest) { String customData = null; if (ApiDefinitionDocType.ALL.name().equals(request.getType()) || ApiDefinitionDocType.MODULE.name().equals(request.getType())) { customData = JSON.toJSONString(request); - shareInfoRequest.setShareType(ShareInfoType.Batch.name()); + shareInfoRequest.setShareType(ShareInfoType.BATCH.name()); } else if (ApiDefinitionDocType.API.name().equals(request.getType())) { apiDefinitionService.checkApiDefinition(request.getApiId()); customData = JSON.toJSONString(request); - shareInfoRequest.setShareType(ShareInfoType.Single.name()); + shareInfoRequest.setShareType(ShareInfoType.SINGLE.name()); } return customData; } - /** * 生成分享连接 * 如果该数据有连接则返回已有的连接,不做有效期判断, 反之创建链接 @@ -87,27 +88,9 @@ public class ApiShareService { */ public ShareInfo genShareInfo(ShareInfo request) { List shareInfos = extShareInfoMapper.selectByShareTypeAndShareApiIdWithBLOBs(request.getShareType(), request.getCustomData(), request.getLang()); - return shareInfos.isEmpty() ? createShareInfo(request) : shareInfos.get(0); + return shareInfos.isEmpty() ? apiReportShareService.createShareInfo(request) : shareInfos.getFirst(); } - public ShareInfo createShareInfo(ShareInfo shareInfo) { - long createTime = System.currentTimeMillis(); - shareInfo.setId(UUID.randomUUID().toString()); - shareInfo.setCreateTime(createTime); - shareInfo.setUpdateTime(createTime); - shareInfoMapper.insert(shareInfo); - return shareInfo; - } - - public ShareInfoDTO conversionShareInfoToDTO(ShareInfo shareInfo) { - ShareInfoDTO returnDTO = new ShareInfoDTO(); - if (null != shareInfo.getCustomData()) { - String url = "?shareId=" + shareInfo.getId(); - returnDTO.setId(shareInfo.getId()); - returnDTO.setShareUrl(url); - } - return returnDTO; - } public ApiDefinitionDocDTO shareDocView(String shareId) { ShareInfo shareInfo = shareInfoMapper.selectByPrimaryKey(shareId); diff --git a/backend/services/api-test/src/main/java/io/metersphere/api/service/definition/ApiReportLogService.java b/backend/services/api-test/src/main/java/io/metersphere/api/service/definition/ApiReportLogService.java new file mode 100644 index 0000000000..9c6dbe73a5 --- /dev/null +++ b/backend/services/api-test/src/main/java/io/metersphere/api/service/definition/ApiReportLogService.java @@ -0,0 +1,85 @@ +package io.metersphere.api.service.definition; + +import io.metersphere.api.domain.ApiReport; +import io.metersphere.api.mapper.ExtApiReportMapper; +import io.metersphere.project.domain.Project; +import io.metersphere.project.mapper.ProjectMapper; +import io.metersphere.sdk.constants.HttpMethodConstants; +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 io.metersphere.system.log.service.OperationLogService; +import jakarta.annotation.Resource; +import org.springframework.stereotype.Service; + +import java.util.ArrayList; +import java.util.List; + +@Service +public class ApiReportLogService { + + @Resource + private ProjectMapper projectMapper; + @Resource + private OperationLogService operationLogService; + @Resource + private ExtApiReportMapper extApiReportMapper; + + + public void deleteLog(ApiReport apiReport) { + Project project = projectMapper.selectByPrimaryKey(apiReport.getProjectId()); + LogDTO dto = new LogDTO( + apiReport.getProjectId(), + project.getOrganizationId(), + apiReport.getId(), + apiReport.getUpdateUser(), + OperationLogType.DELETE.name(), + OperationLogModule.API_REPORT, + apiReport.getName()); + + dto.setPath("/api/report/case/delete/" + apiReport.getId()); + dto.setMethod(HttpMethodConstants.GET.name()); + dto.setOriginalValue(JSON.toJSONBytes(apiReport)); + operationLogService.add(dto); + } + + public void updateLog(ApiReport apiReport) { + Project project = projectMapper.selectByPrimaryKey(apiReport.getProjectId()); + LogDTO dto = new LogDTO( + apiReport.getProjectId(), + project.getOrganizationId(), + apiReport.getId(), + apiReport.getUpdateUser(), + OperationLogType.UPDATE.name(), + OperationLogModule.API_REPORT, + apiReport.getName()); + + dto.setPath("/api/report/case/rename/" + apiReport.getId() + "/" + apiReport.getName()); + dto.setMethod(HttpMethodConstants.GET.name()); + dto.setOriginalValue(JSON.toJSONBytes(apiReport)); + operationLogService.add(dto); + } + + public void batchDeleteLog(List ids, String userId, String projectId) { + Project project = projectMapper.selectByPrimaryKey(projectId); + List apiReports = extApiReportMapper.selectApiReportByIds(ids, true); + List logs = new ArrayList<>(); + apiReports.forEach(apiReport -> { + LogDTO dto = new LogDTO( + projectId, + project.getOrganizationId(), + apiReport.getId(), + userId, + OperationLogType.DELETE.name(), + OperationLogModule.API_REPORT, + apiReport.getName()); + + dto.setPath("/api/report/case/batch/delete"); + dto.setMethod(HttpMethodConstants.POST.name()); + dto.setOriginalValue(JSON.toJSONBytes(apiReport)); + logs.add(dto); + }); + operationLogService.batchAdd(logs); + } +} diff --git a/backend/services/api-test/src/main/java/io/metersphere/api/service/definition/ApiReportService.java b/backend/services/api-test/src/main/java/io/metersphere/api/service/definition/ApiReportService.java index e841b31046..35db8d29e8 100644 --- a/backend/services/api-test/src/main/java/io/metersphere/api/service/definition/ApiReportService.java +++ b/backend/services/api-test/src/main/java/io/metersphere/api/service/definition/ApiReportService.java @@ -1,12 +1,20 @@ package io.metersphere.api.service.definition; -import io.metersphere.api.domain.ApiReport; -import io.metersphere.api.domain.ApiReportStep; +import io.metersphere.api.domain.*; +import io.metersphere.api.dto.definition.ApiReportBatchRequest; +import io.metersphere.api.dto.definition.ApiReportDTO; +import io.metersphere.api.dto.definition.ApiReportDetailDTO; +import io.metersphere.api.dto.definition.ApiReportPageRequest; +import io.metersphere.api.mapper.ApiReportDetailMapper; import io.metersphere.api.mapper.ApiReportMapper; import io.metersphere.api.mapper.ApiReportStepMapper; import io.metersphere.api.mapper.ExtApiReportMapper; +import io.metersphere.api.utils.ApiDataUtils; +import io.metersphere.sdk.dto.api.result.RequestResult; +import io.metersphere.sdk.exception.MSException; +import io.metersphere.sdk.util.BeanUtils; import io.metersphere.sdk.util.SubListUtils; -import io.metersphere.system.dto.sdk.BasePageRequest; +import io.metersphere.sdk.util.Translator; import io.metersphere.system.service.UserLoginService; import jakarta.annotation.Resource; import org.apache.commons.collections.CollectionUtils; @@ -18,10 +26,7 @@ import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Propagation; import org.springframework.transaction.annotation.Transactional; -import java.util.ArrayList; -import java.util.List; -import java.util.Map; -import java.util.Set; +import java.util.*; import java.util.stream.Collectors; import java.util.stream.Stream; @@ -38,6 +43,13 @@ public class ApiReportService { private ExtApiReportMapper extApiReportMapper; @Resource private UserLoginService userLoginService; + @Resource + private ApiReportStepMapper apiReportStepMapper; + @Resource + private ApiReportDetailMapper apiReportDetailMapper; + @Resource + private ApiReportLogService apiReportLogService; + @Transactional(rollbackFor = Exception.class, propagation = Propagation.REQUIRES_NEW) public void insertApiReport(List reports) { @@ -46,8 +58,6 @@ public class ApiReportService { ApiReportMapper reportMapper = sqlSession.getMapper(ApiReportMapper.class); SubListUtils.dealForSubList(reports, 1000, subList -> { subList.forEach(reportMapper::insertSelective); - sqlSession.flushStatements(); - sqlSession.clearCache(); }); sqlSession.flushStatements(); if (sqlSessionFactory != null) { @@ -63,8 +73,6 @@ public class ApiReportService { ApiReportStepMapper stepMapper = sqlSession.getMapper(ApiReportStepMapper.class); SubListUtils.dealForSubList(reportSteps, 1000, subList -> { subList.forEach(stepMapper::insertSelective); - sqlSession.flushStatements(); - sqlSession.clearCache(); }); sqlSession.flushStatements(); if (sqlSessionFactory != null) { @@ -73,8 +81,8 @@ public class ApiReportService { } } - public List getPage(BasePageRequest request, String projectId) { - List list = extApiReportMapper.list(request, projectId); + public List getPage(ApiReportPageRequest request) { + List list = extApiReportMapper.list(request); //取所有的userid Set userSet = list.stream() .flatMap(apiReport -> Stream.of(apiReport.getUpdateUser(), apiReport.getDeleteUser(), apiReport.getCreateUser())) @@ -87,4 +95,99 @@ public class ApiReportService { }); return list; } + + public void rename(String id, String name, String userId) { + ApiReport apiReport = checkResource(id); + apiReport.setName(name); + apiReport.setUpdateTime(System.currentTimeMillis()); + apiReport.setUpdateUser(userId); + apiReportMapper.updateByPrimaryKeySelective(apiReport); + apiReportLogService.updateLog(apiReport); + } + + public void delete(String id, String userId) { + ApiReport apiReport = checkResource(id); + apiReport.setDeleted(true); + apiReport.setDeleteTime(System.currentTimeMillis()); + apiReport.setDeleteUser(userId); + apiReportMapper.updateByPrimaryKeySelective(apiReport); + apiReportLogService.deleteLog(apiReport); + } + + private ApiReport checkResource(String id) { + ApiReport apiReport = apiReportMapper.selectByPrimaryKey(id); + if (apiReport == null) { + throw new RuntimeException(Translator.get("api_case_report_not_exist")); + } + return apiReport; + } + + public void batchDelete(ApiReportBatchRequest request, String userId) { + List ids = doSelectIds(request); + if (CollectionUtils.isEmpty(ids)) { + return; + } + SubListUtils.dealForSubList(ids, 2000, subList -> { + ApiReportExample example = new ApiReportExample(); + example.createCriteria().andIdIn(subList); + ApiReport apiReport = new ApiReport(); + apiReport.setDeleted(true); + apiReport.setDeleteTime(System.currentTimeMillis()); + apiReport.setDeleteUser(userId); + apiReportMapper.updateByExampleSelective(apiReport, example); + //TODO 记录日志 + apiReportLogService.batchDeleteLog(subList, userId, request.getProjectId()); + }); + } + + public List doSelectIds(ApiReportBatchRequest request) { + if (request.isSelectAll()) { + List ids = extApiReportMapper.getIds(request); + if (CollectionUtils.isNotEmpty(request.getExcludeIds())) { + ids.removeAll(request.getExcludeIds()); + } + return ids; + } else { + request.getSelectIds().removeAll(request.getExcludeIds()); + return request.getSelectIds(); + } + } + + public ApiReportDTO get(String id) { + ApiReportDTO apiReportDTO = new ApiReportDTO(); + ApiReport apiReport = checkResource(id); + BeanUtils.copyBean(apiReportDTO, apiReport); + //需要查询出所有的步骤 + ApiReportStepExample apiReportStep = new ApiReportStepExample(); + apiReportStep.createCriteria().andReportIdEqualTo(id); + List apiReportSteps = apiReportStepMapper.selectByExample(apiReportStep); + if (CollectionUtils.isEmpty(apiReportSteps)) { + throw new MSException(Translator.get("api_case_report_not_exist")); + } + apiReportSteps.sort(Comparator.comparingLong(ApiReportStep::getSort)); + apiReportDTO.setChildren(apiReportSteps); + return apiReportDTO; + } + + public List getDetail(String stepId, String reportId) { + List apiReportDetails = checkResourceStep(stepId, reportId); + List results = new ArrayList<>(); + apiReportDetails.forEach(apiReportDetail -> { + ApiReportDetailDTO apiReportDetailDTO = new ApiReportDetailDTO(); + BeanUtils.copyBean(apiReportDetailDTO, apiReportDetail); + apiReportDetailDTO.setContent(ApiDataUtils.parseObject(new String(apiReportDetail.getContent()), RequestResult.class)); + results.add(apiReportDetailDTO); + }); + return results; + } + + private List checkResourceStep(String stepId, String reportId) { + ApiReportDetailExample apiReportDetailExample = new ApiReportDetailExample(); + apiReportDetailExample.createCriteria().andStepIdEqualTo(stepId).andReportIdEqualTo(reportId); + List apiReportDetails = apiReportDetailMapper.selectByExampleWithBLOBs(apiReportDetailExample); + if (CollectionUtils.isEmpty(apiReportDetails)) { + throw new MSException(Translator.get("api_case_report_not_exist")); + } + return apiReportDetails; + } } diff --git a/backend/services/api-test/src/main/java/io/metersphere/api/service/scenario/ApiScenarioReportService.java b/backend/services/api-test/src/main/java/io/metersphere/api/service/scenario/ApiScenarioReportService.java index 0157d3249c..c2a53d00ca 100644 --- a/backend/services/api-test/src/main/java/io/metersphere/api/service/scenario/ApiScenarioReportService.java +++ b/backend/services/api-test/src/main/java/io/metersphere/api/service/scenario/ApiScenarioReportService.java @@ -31,8 +31,6 @@ public class ApiScenarioReportService { ApiScenarioReportMapper reportMapper = sqlSession.getMapper(ApiScenarioReportMapper.class); SubListUtils.dealForSubList(reports, 1000, subList -> { subList.forEach(reportMapper::insertSelective); - sqlSession.flushStatements(); - sqlSession.clearCache(); }); sqlSession.flushStatements(); if (sqlSessionFactory != null) { @@ -48,8 +46,6 @@ public class ApiScenarioReportService { ApiScenarioReportStepMapper stepMapper = sqlSession.getMapper(ApiScenarioReportStepMapper.class); SubListUtils.dealForSubList(reportSteps, 1000, subList -> { subList.forEach(stepMapper::insertSelective); - sqlSession.flushStatements(); - sqlSession.clearCache(); }); sqlSession.flushStatements(); if (sqlSessionFactory != null) { diff --git a/backend/services/api-test/src/main/resources/permission.json b/backend/services/api-test/src/main/resources/permission.json index 292a2b7593..fccb33c471 100644 --- a/backend/services/api-test/src/main/resources/permission.json +++ b/backend/services/api-test/src/main/resources/permission.json @@ -129,6 +129,21 @@ "id": "PROJECT_API_SCENARIO:READ+DEBUG" } ] + }, + { + "id": "PROJECT_API_REPORT", + "name": "permission.api_report", + "permissions": [ + { + "id": "PROJECT_API_REPORT:READ" + }, + { + "id": "PROJECT_API_REPORT:READ+UPDATE" + }, + { + "id": "PROJECT_API_REPORT:READ+DELETE" + } + ] } ] } diff --git a/backend/services/api-test/src/test/java/io/metersphere/api/controller/ApiDefinitionScheduleControllerTests.java b/backend/services/api-test/src/test/java/io/metersphere/api/controller/ApiDefinitionScheduleControllerTests.java index 3ca943959b..1e552ae936 100644 --- a/backend/services/api-test/src/test/java/io/metersphere/api/controller/ApiDefinitionScheduleControllerTests.java +++ b/backend/services/api-test/src/test/java/io/metersphere/api/controller/ApiDefinitionScheduleControllerTests.java @@ -59,12 +59,12 @@ public class ApiDefinitionScheduleControllerTests extends BaseTest { example.createCriteria().andProjectIdEqualTo(DEFAULT_PROJECT_ID); List apiDefinitionSwaggers = apiDefinitionSwaggerMapper.selectByExample(example); assert apiDefinitionSwaggers.size() == 1; - ID = apiDefinitionSwaggers.get(0).getId(); + ID = apiDefinitionSwaggers.getFirst().getId(); ScheduleExample scheduleExample = new ScheduleExample(); - scheduleExample.createCriteria().andResourceIdEqualTo(apiDefinitionSwaggers.get(0).getId()); + scheduleExample.createCriteria().andResourceIdEqualTo(apiDefinitionSwaggers.getFirst().getId()); List schedules = scheduleMapper.selectByExample(scheduleExample); assert schedules.size() == 1; - SCHEDULE_ID = schedules.get(0).getId(); + SCHEDULE_ID = schedules.getFirst().getId(); request = new ApiScheduleRequest(); request.setName("定时任务2"); diff --git a/backend/services/api-test/src/test/java/io/metersphere/api/controller/ApiReportControllerTests.java b/backend/services/api-test/src/test/java/io/metersphere/api/controller/ApiReportControllerTests.java index 5809995038..1d51a5094f 100644 --- a/backend/services/api-test/src/test/java/io/metersphere/api/controller/ApiReportControllerTests.java +++ b/backend/services/api-test/src/test/java/io/metersphere/api/controller/ApiReportControllerTests.java @@ -1,19 +1,29 @@ package io.metersphere.api.controller; -import io.metersphere.api.domain.ApiReport; -import io.metersphere.api.domain.ApiReportExample; -import io.metersphere.api.domain.ApiReportStep; -import io.metersphere.api.domain.ApiReportStepExample; +import io.metersphere.api.constants.ShareInfoType; +import io.metersphere.api.domain.*; +import io.metersphere.api.dto.definition.ApiReportBatchRequest; +import io.metersphere.api.dto.definition.ApiReportDTO; +import io.metersphere.api.dto.definition.ApiReportDetailDTO; +import io.metersphere.api.dto.definition.ApiReportPageRequest; import io.metersphere.api.dto.scenario.ApiScenarioDTO; +import io.metersphere.api.dto.share.ShareInfoDTO; +import io.metersphere.api.mapper.ApiReportDetailMapper; import io.metersphere.api.mapper.ApiReportMapper; import io.metersphere.api.mapper.ApiReportStepMapper; import io.metersphere.api.service.definition.ApiReportService; +import io.metersphere.api.utils.ApiDataUtils; +import io.metersphere.project.domain.ProjectApplication; +import io.metersphere.project.domain.ProjectApplicationExample; +import io.metersphere.project.mapper.ProjectApplicationMapper; import io.metersphere.sdk.constants.ApiReportStatus; +import io.metersphere.sdk.constants.PermissionConstants; import io.metersphere.sdk.constants.SessionConstants; +import io.metersphere.sdk.domain.ShareInfo; +import io.metersphere.sdk.mapper.ShareInfoMapper; import io.metersphere.sdk.util.JSON; import io.metersphere.system.base.BaseTest; import io.metersphere.system.controller.handler.ResultHolder; -import io.metersphere.system.dto.sdk.BasePageRequest; import io.metersphere.system.utils.Pager; import jakarta.annotation.Resource; import org.junit.jupiter.api.*; @@ -21,6 +31,7 @@ import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMock import org.springframework.boot.test.context.SpringBootTest; import org.springframework.http.MediaType; import org.springframework.test.web.servlet.MvcResult; +import org.springframework.test.web.servlet.ResultActions; import org.springframework.test.web.servlet.request.MockMvcRequestBuilders; import java.nio.charset.StandardCharsets; @@ -42,6 +53,21 @@ public class ApiReportControllerTests extends BaseTest { private ApiReportMapper apiReportMapper; @Resource private ApiReportStepMapper apiReportStepMapper; + @Resource + private ApiReportDetailMapper apiReportDetailMapper; + @Resource + private ShareInfoMapper shareInfoMapper; + @Resource + private ProjectApplicationMapper projectApplicationMapper; + + private static final String BASIC = "/api/report/case"; + private static final String PAGE = BASIC + "/page"; + private static final String RENAME = BASIC + "/rename/"; + private static final String DELETE = BASIC + "/delete/"; + private static final String GET = BASIC + "/get/"; + private static final String BATCH_DELETE = BASIC + "/batch/delete"; + private static final String DETAIL = BASIC + "/get/detail/"; + @Test @Order(1) @@ -77,7 +103,7 @@ public class ApiReportControllerTests extends BaseTest { for (int i = 0; i < 1515; i++) { ApiReportStep apiReportStep = new ApiReportStep(); apiReportStep.setStepId("api-report-step-id" + i); - apiReportStep.setReportId("api-report-id" + i); + apiReportStep.setReportId("api-report-id-success" + i); apiReportStep.setSort(0L); apiReportStep.setStepType("case"); steps.add(apiReportStep); @@ -91,7 +117,6 @@ public class ApiReportControllerTests extends BaseTest { return mockMvc.perform(MockMvcRequestBuilders.post(url) .header(SessionConstants.HEADER_TOKEN, sessionId) .header(SessionConstants.CSRF_TOKEN, csrfToken) - .header(SessionConstants.CURRENT_PROJECT, DEFAULT_PROJECT_ID) .content(JSON.toJSONString(param)) .contentType(MediaType.APPLICATION_JSON)) .andExpect(status().isOk()) @@ -113,10 +138,11 @@ public class ApiReportControllerTests extends BaseTest { @Test @Order(2) public void testGetPage() throws Exception { - BasePageRequest request = new BasePageRequest(); + ApiReportPageRequest request = new ApiReportPageRequest(); request.setCurrent(1); request.setPageSize(10); - MvcResult mvcResult = responsePost("/api/report/case/page", request); + request.setProjectId(DEFAULT_PROJECT_ID); + MvcResult mvcResult = responsePost(PAGE, request); Pager returnPager = parseObjectFromMvcResult(mvcResult, Pager.class); //返回值不为空 Assertions.assertNotNull(returnPager); @@ -127,18 +153,245 @@ public class ApiReportControllerTests extends BaseTest { Assertions.assertTrue(((List) returnPager.getList()).size() <= request.getPageSize()); //过滤 request.setFilter(new HashMap<>() {{ - put("status", List.of("SUCCESS", "ERROR")); + put("status", List.of(ApiReportStatus.SUCCESS.name(), ApiReportStatus.ERROR.name())); }}); - mvcResult = responsePost("/api/report/case/page", request); + mvcResult = responsePost(PAGE, request); returnPager = parseObjectFromMvcResult(mvcResult, Pager.class); //返回值不为空 Assertions.assertNotNull(returnPager); Assertions.assertTrue(((List) returnPager.getList()).size() <= request.getPageSize()); List list = JSON.parseArray(JSON.toJSONString(returnPager.getList()), ApiReport.class); list.forEach(apiReport -> { - Assertions.assertTrue(apiReport.getStatus().equals("SUCCESS") || apiReport.getStatus().equals("ERROR")); + Assertions.assertTrue(apiReport.getStatus().equals(ApiReportStatus.SUCCESS.name()) || apiReport.getStatus().equals(ApiReportStatus.ERROR.name())); }); + //校验权限 + requestPostPermissionTest(PermissionConstants.PROJECT_API_REPORT_READ, PAGE, request); + } + + protected ResultActions requestGetWithOk(String url, Object... uriVariables) throws Exception { + return mockMvc.perform(getRequestBuilder(url, uriVariables)) + .andExpect(content().contentType(MediaType.APPLICATION_JSON)) + .andExpect(status().isOk()); + } + + @Test + @Order(3) + public void testRename() throws Exception { + // @@请求成功 + String newName = "api-report-new-name"; + requestGetWithOk(RENAME + "api-report-id0" + "/" + newName); + ApiReport apiReport = apiReportMapper.selectByPrimaryKey("api-report-id0"); + Assertions.assertNotNull(apiReport); + Assertions.assertEquals(apiReport.getName(), newName); + mockMvc.perform(getRequestBuilder(RENAME + "api-report" + "/" + newName)) + .andExpect(content().contentType(MediaType.APPLICATION_JSON)) + .andExpect(status().is5xxServerError()); + // @@校验权限 + requestGetPermissionTest(PermissionConstants.PROJECT_API_REPORT_UPDATE, RENAME + "api-report-id0" + "/" + newName); + } + + @Test + @Order(4) + public void testDeleted() throws Exception { + // @@请求成功 + requestGetWithOk(DELETE + "api-report-id0"); + ApiReport apiReport = apiReportMapper.selectByPrimaryKey("api-report-id0"); + Assertions.assertNotNull(apiReport); + Assertions.assertTrue(apiReport.getDeleted()); + mockMvc.perform(getRequestBuilder(RENAME + "api-report")) + .andExpect(content().contentType(MediaType.APPLICATION_JSON)) + .andExpect(status().is5xxServerError()); + // @@校验权限 + requestGetPermissionTest(PermissionConstants.PROJECT_API_REPORT_DELETE, DELETE + "api-report-id1"); + } + + @Test + @Order(5) + public void testBatch() throws Exception { + // @@请求成功 + ApiReportBatchRequest request = new ApiReportBatchRequest(); + request.setProjectId(DEFAULT_PROJECT_ID); + request.setSelectIds(List.of("api-report-id3")); + request.setExcludeIds(List.of("api-report-id3")); + responsePost(BATCH_DELETE, request); + request.setSelectIds(List.of("api-report-id4")); + responsePost(BATCH_DELETE, request); + ApiReport apiReport = apiReportMapper.selectByPrimaryKey("api-report-id4"); + Assertions.assertNotNull(apiReport); + Assertions.assertTrue(apiReport.getDeleted()); + request.setSelectAll(true); + responsePost(BATCH_DELETE, request); + // @@校验权限 + requestPostPermissionTest(PermissionConstants.PROJECT_API_REPORT_DELETE, BATCH_DELETE, request); + } + + @Test + @Order(6) + public void testGet() throws Exception { + // @@请求成功 + List reports = new ArrayList<>(); + ApiReport apiReport = new ApiReport(); + apiReport.setId("test-report-id"); + apiReport.setProjectId(DEFAULT_PROJECT_ID); + apiReport.setName("test-report-name"); + apiReport.setStartTime(System.currentTimeMillis()); + apiReport.setResourceId("test-resource-id"); + apiReport.setCreateUser("admin"); + apiReport.setUpdateUser("admin"); + apiReport.setUpdateTime(System.currentTimeMillis()); + apiReport.setPoolId("api-pool-id"); + apiReport.setEnvironmentId("api-environment-id"); + apiReport.setRunMode("api-run-mode"); + apiReport.setStatus(ApiReportStatus.SUCCESS.name()); + apiReport.setTriggerMode("api-trigger-mode"); + apiReport.setVersionId("api-version-id"); + reports.add(apiReport); + apiReportService.insertApiReport(reports); + List steps = new ArrayList<>(); + for (int i = 0; i < 5; i++) { + ApiReportStep apiReportStep = new ApiReportStep(); + apiReportStep.setStepId("test-report-step-id" + i); + apiReportStep.setReportId("test-report-id"); + apiReportStep.setSort((long) i); + apiReportStep.setStepType("case"); + steps.add(apiReportStep); + } + + apiReportService.insertApiReportStep(steps); + MvcResult mvcResult = this.requestGetWithOk(GET + "test-report-id") + .andReturn(); + ApiReportDTO apiReportDTO = ApiDataUtils.parseObject(JSON.toJSONString(parseResponse(mvcResult).get("data")), ApiReportDTO.class); + + Assertions.assertNotNull(apiReportDTO); + Assertions.assertEquals(apiReportDTO.getId(), "test-report-id"); + + mockMvc.perform(getRequestBuilder(GET + "test")) + .andExpect(content().contentType(MediaType.APPLICATION_JSON)) + .andExpect(status().is5xxServerError()); + + mockMvc.perform(getRequestBuilder(GET + "api-report-id10")) + .andExpect(content().contentType(MediaType.APPLICATION_JSON)) + .andExpect(status().is5xxServerError()); + + // @@校验权限 + requestGetPermissionTest(PermissionConstants.PROJECT_API_REPORT_READ, GET + "api-report-id0"); + } + + @Test + @Order(6) + public void testGetDetail() throws Exception { + // @@请求成功 + List reports = new ArrayList<>(); + for (int i = 0; i < 2; i++) { + ApiReportDetail apiReportDetail = new ApiReportDetail(); + apiReportDetail.setId("test-report-detail-id" + i); + apiReportDetail.setReportId("test-report-id"); + apiReportDetail.setStepId("test-report-step-id1"); + apiReportDetail.setStatus("success"); + apiReportDetail.setResponseSize(0L); + apiReportDetail.setRequestTime((long) i); + apiReportDetail.setContent("{\"resourceId\":\"\",\"stepId\":null,\"threadName\":\"Thread Group\",\"name\":\"HTTP Request1\",\"url\":\"https://www.baidu.com/\",\"requestSize\":195,\"startTime\":1705570589125,\"endTime\":1705570589310,\"error\":1,\"headers\":\"Connection: keep-alive\\nContent-Length: 0\\nContent-Type: application/x-www-form-urlencoded; charset=UTF-8\\nHost: www.baidu.com\\nUser-Agent: Apache-HttpClient/4.5.14 (Java/21)\\n\",\"cookies\":\"\",\"body\":\"POST https://www.baidu.com/\\n\\nPOST data:\\n\\n\\n[no cookies]\\n\",\"status\":\"ERROR\",\"method\":\"POST\",\"assertionTotal\":1,\"passAssertionsTotal\":0,\"subRequestResults\":[],\"responseResult\":{\"responseCode\":\"200\",\"responseMessage\":\"OK\",\"responseTime\":185,\"latency\":180,\"responseSize\":2559,\"headers\":\"HTTP/1.1 200 OK\\nContent-Length: 2443\\nContent-Type: text/html\\nServer: bfe\\nDate: Thu, 18 Jan 2024 09:36:29 GMT\\n\",\"body\":\"\\r\\n 百度一下,你就知道

关于百度 About Baidu

©2017 Baidu 使用百度前必读  意见反馈 京ICP证030173号 

\\r\\n\",\"contentType\":\"text/html\",\"vars\":null,\"imageUrl\":null,\"socketInitTime\":14,\"dnsLookupTime\":0,\"tcpHandshakeTime\":0,\"sslHandshakeTime\":0,\"transferStartTime\":166,\"downloadTime\":5,\"bodySize\":2443,\"headerSize\":116,\"assertions\":[{\"name\":\"JSON Assertion\",\"content\":null,\"script\":null,\"message\":\"Expected to find an object with property ['test'] in path $ but found 'java.lang.String'. This is not a json object according to the JsonProvider: 'com.jayway.jsonpath.spi.json.JsonSmartJsonProvider'.\",\"pass\":false}]},\"isSuccessful\":false,\"fakeErrorMessage\":\"\",\"fakeErrorCode\":null}\n".getBytes()); + reports.add(apiReportDetail); + } + apiReportDetailMapper.batchInsert(reports); + + MvcResult mvcResult = this.requestGetWithOk(DETAIL + "test-report-id" + "/" + "test-report-step-id1") + .andReturn(); + List data = ApiDataUtils.parseArray(JSON.toJSONString(parseResponse(mvcResult).get("data")), ApiReportDetailDTO.class); + Assertions.assertNotNull(data); + + mockMvc.perform(getRequestBuilder(DETAIL + "test")) + .andExpect(content().contentType(MediaType.APPLICATION_JSON)) + .andExpect(status().is5xxServerError()); + + mockMvc.perform(getRequestBuilder(DETAIL + "api-report-id10")) + .andExpect(content().contentType(MediaType.APPLICATION_JSON)) + .andExpect(status().is5xxServerError()); + + // @@校验权限 + requestGetPermissionTest(PermissionConstants.PROJECT_API_REPORT_READ, GET + "api-report-id0"); + } + + //分享报告 + @Test + @Order(7) + public void generateUrl() throws Exception { + ShareInfo shareInfo = new ShareInfo(); + shareInfo.setCustomData("test-report-id".getBytes()); + shareInfo.setProjectId(DEFAULT_PROJECT_ID); + shareInfo.setShareType(ShareInfoType.API_SHARE_REPORT.name()); + MvcResult mvcResult = responsePost("/api/report/share/gen", shareInfo); + ShareInfoDTO shareInfoDTO = parseObjectFromMvcResult(mvcResult, ShareInfoDTO.class); + Assertions.assertNotNull(shareInfoDTO); + Assertions.assertNotNull(shareInfoDTO.getShareUrl()); + Assertions.assertNotNull(shareInfoDTO.getId()); + String shareId = shareInfoDTO.getId(); + MvcResult mvcResult1 = this.requestGetWithOk(GET + shareId + "/" + "test-report-id") + .andReturn(); + ApiReportDTO apiReportDTO = ApiDataUtils.parseObject(JSON.toJSONString(parseResponse(mvcResult1).get("data")), ApiReportDTO.class); + Assertions.assertNotNull(apiReportDTO); + Assertions.assertEquals(apiReportDTO.getId(), "test-report-id"); + + this.requestGetWithOk("/api/report/share/get/" + shareId) + .andReturn(); + + mockMvc.perform(getRequestBuilder("/api/report/share/get/" + "test")) + .andExpect(content().contentType(MediaType.APPLICATION_JSON)) + .andExpect(status().is5xxServerError()); + + mvcResult = this.requestGetWithOk(DETAIL + shareId + "/" + "test-report-id" + "/" + "test-report-step-id1") + .andReturn(); + List data = ApiDataUtils.parseArray(JSON.toJSONString(parseResponse(mvcResult).get("data")), ApiReportDetailDTO.class); + Assertions.assertNotNull(data); + //过期时间时间戳 1702950953000 2023-12-19 09:55:53 + ShareInfo shareInfo1 = shareInfoMapper.selectByPrimaryKey(shareId); + shareInfo1.setUpdateTime(1702950953000L); + shareInfoMapper.updateByPrimaryKey(shareInfo1); + + mockMvc.perform(getRequestBuilder(GET + shareId + "/" + "test-report-id")) + .andExpect(content().contentType(MediaType.APPLICATION_JSON)) + .andExpect(status().is5xxServerError()); + + //TODO 过期的校验 未完成 需要补充 + //项目当前设置了分享时间 并且没有过期 + + shareInfo = new ShareInfo(); + shareInfo.setCustomData("test-report-id".getBytes()); + shareInfo.setProjectId(DEFAULT_PROJECT_ID); + shareInfo.setShareType(ShareInfoType.API_SHARE_REPORT.name()); + mvcResult = responsePost("/api/report/share/gen", shareInfo); + shareInfoDTO = parseObjectFromMvcResult(mvcResult, ShareInfoDTO.class); + Assertions.assertNotNull(shareInfoDTO); + Assertions.assertNotNull(shareInfoDTO.getShareUrl()); + Assertions.assertNotNull(shareInfoDTO.getId()); + shareId = shareInfoDTO.getId(); + + ProjectApplicationExample projectApplicationExample = new ProjectApplicationExample(); + projectApplicationExample.createCriteria().andProjectIdEqualTo(DEFAULT_PROJECT_ID).andTypeEqualTo(ShareInfoType.API_SHARE_REPORT.name()); + List projectApplications = projectApplicationMapper.selectByExample(projectApplicationExample); + if (projectApplications.isEmpty()) { + ProjectApplication projectApplication = new ProjectApplication(); + projectApplication.setProjectId(DEFAULT_PROJECT_ID); + projectApplication.setType(ShareInfoType.API_SHARE_REPORT.name()); + projectApplication.setTypeValue("1D"); + projectApplicationMapper.insert(projectApplication); + } + + mvcResult1 = this.requestGetWithOk(GET + shareId + "/" + "test-report-id") + .andReturn(); + apiReportDTO = ApiDataUtils.parseObject(JSON.toJSONString(parseResponse(mvcResult1).get("data")), ApiReportDTO.class); + Assertions.assertNotNull(apiReportDTO); + Assertions.assertEquals(apiReportDTO.getId(), "test-report-id"); + + //项目当前设置了分享时间 并且过期 + shareInfo1 = shareInfoMapper.selectByPrimaryKey(shareId); + shareInfo1.setUpdateTime(1702950953000L); + shareInfoMapper.updateByPrimaryKey(shareInfo1); + + mockMvc.perform(getRequestBuilder(GET + shareId + "/" + "test-report-id")) + .andExpect(content().contentType(MediaType.APPLICATION_JSON)) + .andExpect(status().is5xxServerError()); } } diff --git a/backend/services/api-test/src/test/java/io/metersphere/api/controller/ApiShareControllerTests.java b/backend/services/api-test/src/test/java/io/metersphere/api/controller/ApiShareControllerTests.java index 46b41f3e1b..eeba26a8f9 100644 --- a/backend/services/api-test/src/test/java/io/metersphere/api/controller/ApiShareControllerTests.java +++ b/backend/services/api-test/src/test/java/io/metersphere/api/controller/ApiShareControllerTests.java @@ -35,7 +35,7 @@ import org.springframework.test.web.servlet.MvcResult; import java.util.List; -@SpringBootTest(webEnvironment= SpringBootTest.WebEnvironment.RANDOM_PORT) +@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) @TestMethodOrder(MethodOrderer.OrderAnnotation.class) @AutoConfigureMockMvc public class ApiShareControllerTests extends BaseTest { @@ -78,7 +78,7 @@ public class ApiShareControllerTests extends BaseTest { ApiDataUtils.setResolver(MsHTTPElement.class); ShareInfoDTO shareInfoDTO = ApiDataUtils.parseObject(JSON.toJSONString(parseResponse(mvcResult).get("data")), ShareInfoDTO.class); // 校验数据是否正确 - List shareInfos = extShareInfoMapper.selectByShareTypeAndShareApiIdWithBLOBs(ShareInfoType.Single.name(), JSON.toJSONString(request).getBytes(), "zh_CN"); + List shareInfos = extShareInfoMapper.selectByShareTypeAndShareApiIdWithBLOBs(ShareInfoType.SINGLE.name(), JSON.toJSONString(request).getBytes(), "zh_CN"); Assertions.assertNotNull(shareInfos); Assertions.assertEquals(1, shareInfos.size()); Assertions.assertEquals(shareInfoDTO.getId(), shareInfos.get(0).getId()); @@ -96,7 +96,7 @@ public class ApiShareControllerTests extends BaseTest { ApiDataUtils.setResolver(MsHTTPElement.class); ShareInfoDTO shareInfoDTOModule = ApiDataUtils.parseObject(JSON.toJSONString(parseResponse(mvcResultModule).get("data")), ShareInfoDTO.class); // 校验数据是否正确 - List shareInfosModule = extShareInfoMapper.selectByShareTypeAndShareApiIdWithBLOBs(ShareInfoType.Batch.name(), JSON.toJSONString(request).getBytes(), "zh_CN"); + List shareInfosModule = extShareInfoMapper.selectByShareTypeAndShareApiIdWithBLOBs(ShareInfoType.BATCH.name(), JSON.toJSONString(request).getBytes(), "zh_CN"); Assertions.assertNotNull(shareInfosModule); Assertions.assertEquals(1, shareInfosModule.size()); Assertions.assertEquals(shareInfoDTOModule.getId(), shareInfosModule.get(0).getId()); @@ -112,7 +112,7 @@ public class ApiShareControllerTests extends BaseTest { ShareInfoDTO allShareInfoDTO = ApiDataUtils.parseObject(JSON.toJSONString(parseResponse(mvcResultAll).get("data")), ShareInfoDTO.class); // 校验数据是否正确 - List allShareInfos = extShareInfoMapper.selectByShareTypeAndShareApiIdWithBLOBs(ShareInfoType.Batch.name(), JSON.toJSONString(request).getBytes(), "zh_CN"); + List allShareInfos = extShareInfoMapper.selectByShareTypeAndShareApiIdWithBLOBs(ShareInfoType.BATCH.name(), JSON.toJSONString(request).getBytes(), "zh_CN"); Assertions.assertNotNull(allShareInfos); Assertions.assertEquals(1, allShareInfos.size()); Assertions.assertEquals(allShareInfoDTO.getId(), allShareInfos.get(0).getId()); @@ -135,14 +135,14 @@ public class ApiShareControllerTests extends BaseTest { ApiDefinitionDocDTO copyAllApiDefinitionDocDTO = new ApiDefinitionDocDTO(); List allList = extApiDefinitionMapper.listDoc(apiDefinitionDocRequest); - if(null != allList){ + if (null != allList) { ApiDefinitionDTO info = allList.stream().findFirst().orElseThrow(() -> new MSException(ApiResultCode.API_DEFINITION_NOT_EXIST)); ApiDefinitionBlob allApiDefinitionBlob = apiDefinitionBlobMapper.selectByPrimaryKey(info.getId()); - if(allApiDefinitionBlob != null){ + if (allApiDefinitionBlob != null) { info.setRequest(ApiDataUtils.parseObject(new String(allApiDefinitionBlob.getRequest()), AbstractMsTestElement.class)); info.setResponse(ApiDataUtils.parseArray(new String(allApiDefinitionBlob.getResponse()), HttpResponse.class)); } - if(StringUtils.isBlank(copyAllApiDefinitionDocDTO.getDocTitle())){ + if (StringUtils.isBlank(copyAllApiDefinitionDocDTO.getDocTitle())) { copyAllApiDefinitionDocDTO.setDocTitle(Translator.get(ALL_API)); } copyAllApiDefinitionDocDTO.setType(ApiDefinitionDocType.ALL.name()); diff --git a/backend/services/project-management/src/main/java/io/metersphere/project/dto/environment/KeyValueEnableParam.java b/backend/services/project-management/src/main/java/io/metersphere/project/dto/environment/KeyValueEnableParam.java index e572e73b2e..2a347afb0f 100644 --- a/backend/services/project-management/src/main/java/io/metersphere/project/dto/environment/KeyValueEnableParam.java +++ b/backend/services/project-management/src/main/java/io/metersphere/project/dto/environment/KeyValueEnableParam.java @@ -12,4 +12,8 @@ public class KeyValueEnableParam extends KeyValueParam { * 描述 */ private String description; + /** + * 是否必填 + */ + private Boolean required = false; } diff --git a/backend/services/project-management/src/main/java/io/metersphere/project/dto/environment/datasource/DataSource.java b/backend/services/project-management/src/main/java/io/metersphere/project/dto/environment/datasource/DataSource.java index 978d7a2896..97ec343640 100644 --- a/backend/services/project-management/src/main/java/io/metersphere/project/dto/environment/datasource/DataSource.java +++ b/backend/services/project-management/src/main/java/io/metersphere/project/dto/environment/datasource/DataSource.java @@ -16,13 +16,11 @@ public class DataSource implements Serializable { @Schema(description = "数据源名称") private String dataSource; @Schema(description = "数据驱动") - @NotBlank(message = "{environment_datasource.driver.not_blank}") private String driver; @Schema(description = "数据驱动id") @NotBlank(message = "{environment_datasource.driverId.not_blank}") private String driverId; @Schema(description = "数据库连接url") - @NotBlank(message = "{environment_datasource.dbUrl.not_blank}") private String dbUrl; @Schema(description = "用户名") private String username; diff --git a/backend/services/project-management/src/main/java/io/metersphere/project/service/EnvironmentService.java b/backend/services/project-management/src/main/java/io/metersphere/project/service/EnvironmentService.java index ba4fdfa193..e5ad8f5046 100644 --- a/backend/services/project-management/src/main/java/io/metersphere/project/service/EnvironmentService.java +++ b/backend/services/project-management/src/main/java/io/metersphere/project/service/EnvironmentService.java @@ -175,7 +175,7 @@ public class EnvironmentService { String baseUrl = baseSystemConfigDTO.getUrl(); if (StringUtils.isNotEmpty(baseUrl)) { Project project = projectMapper.selectByPrimaryKey(environment.getProjectId()); - environmentInfoDTO.getConfig().getHttpConfig().get(0).setUrl(StringUtils.join(baseUrl, MOCK_EVN_SOCKET, project.getNum())); + environmentInfoDTO.getConfig().getHttpConfig().getFirst().setUrl(StringUtils.join(baseUrl, MOCK_EVN_SOCKET, project.getNum())); } } @@ -194,7 +194,7 @@ public class EnvironmentService { String fileName = null; if (CollectionUtils.isNotEmpty(environmentList)) { if (environmentList.size() == 1) { - fileName = StringUtils.join(project.getName(), "_", environmentList.get(0).getName(), ".json"); + fileName = StringUtils.join(project.getName(), "_", environmentList.getFirst().getName(), ".json"); } else { fileName = StringUtils.join(project.getName(), "_", Translator.get("env_info_all")); } @@ -267,7 +267,7 @@ public class EnvironmentService { environmentExample.createCriteria().andNameEqualTo(environment.getName()).andProjectIdEqualTo(environment.getProjectId()).andIdNotEqualTo(environment.getId()); List environments = environmentMapper.selectByExample(environmentExample); if (CollectionUtils.isNotEmpty(environments)) { - return environments.get(0); + return environments.getFirst(); } return null; } diff --git a/backend/services/project-management/src/main/java/io/metersphere/project/service/FileMetadataService.java b/backend/services/project-management/src/main/java/io/metersphere/project/service/FileMetadataService.java index 10cbf17717..9168a29b89 100644 --- a/backend/services/project-management/src/main/java/io/metersphere/project/service/FileMetadataService.java +++ b/backend/services/project-management/src/main/java/io/metersphere/project/service/FileMetadataService.java @@ -614,7 +614,7 @@ public class FileMetadataService { fileMetadataExample.createCriteria().andRefIdEqualTo(refId).andLatestEqualTo(true); List fileMetadataList = fileMetadataMapper.selectByExample(fileMetadataExample); if (CollectionUtils.isNotEmpty(fileMetadataList)) { - return fileMetadataList.get(0); + return fileMetadataList.getFirst(); } else { throw new MSException(Translator.get("latest.file.not.exist")); } diff --git a/backend/services/project-management/src/main/java/io/metersphere/project/service/GlobalParamsService.java b/backend/services/project-management/src/main/java/io/metersphere/project/service/GlobalParamsService.java index 0102fb774c..ef2a346c8f 100644 --- a/backend/services/project-management/src/main/java/io/metersphere/project/service/GlobalParamsService.java +++ b/backend/services/project-management/src/main/java/io/metersphere/project/service/GlobalParamsService.java @@ -81,8 +81,8 @@ public class GlobalParamsService { if (CollectionUtils.isNotEmpty(projectParameters)) { GlobalParamsDTO globalParamsDTO = new GlobalParamsDTO(); globalParamsDTO.setProjectId(projectId); - globalParamsDTO.setId(projectParameters.get(0).getId()); - globalParamsDTO.setGlobalParams(JSON.parseObject(new String(projectParameters.get(0).getParameters()), GlobalParams.class)); + globalParamsDTO.setId(projectParameters.getFirst().getId()); + globalParamsDTO.setGlobalParams(JSON.parseObject(new String(projectParameters.getFirst().getParameters()), GlobalParams.class)); return globalParamsDTO; } else { return null; @@ -116,8 +116,8 @@ public class GlobalParamsService { if (CollectionUtils.isNotEmpty(projectParameters)) { GlobalParamsDTO globalParamsDTO = new GlobalParamsDTO(); globalParamsDTO.setProjectId(projectId); - globalParamsDTO.setId(projectParameters.get(0).getId()); - globalParamsDTO.setGlobalParams(JSON.parseObject(new String(projectParameters.get(0).getParameters()), GlobalParams.class)); + globalParamsDTO.setId(projectParameters.getFirst().getId()); + globalParamsDTO.setGlobalParams(JSON.parseObject(new String(projectParameters.getFirst().getParameters()), GlobalParams.class)); bytes = JSON.toJSONString(globalParamsDTO).getBytes(); } else { throw new MSException(Translator.get("global_parameters_is_not_exist")); diff --git a/backend/services/project-management/src/main/java/io/metersphere/project/service/ProjectService.java b/backend/services/project-management/src/main/java/io/metersphere/project/service/ProjectService.java index d1fa413b35..cb0d2ea8d7 100644 --- a/backend/services/project-management/src/main/java/io/metersphere/project/service/ProjectService.java +++ b/backend/services/project-management/src/main/java/io/metersphere/project/service/ProjectService.java @@ -167,8 +167,8 @@ public class ProjectService { TestResourcePoolExample.Criteria criteria = example.createCriteria(); criteria.andIdIn(poolIds).andEnableEqualTo(true).andDeletedEqualTo(false); List testResourcePools = new ArrayList<>(); - testResourcePools = switch (type) { - case ApplicationScope.API_TEST-> { + testResourcePools = switch (type) { + case ApplicationScope.API_TEST -> { criteria.andApiTestEqualTo(true); yield testResourcePoolMapper.selectByExample(example); } @@ -183,7 +183,7 @@ public class ProjectService { default -> new ArrayList<>(); }; return testResourcePools.stream().map(testResourcePool -> - new OptionDTO(testResourcePool.getId(), testResourcePool.getName()) + new OptionDTO(testResourcePool.getId(), testResourcePool.getName()) ).toList(); } @@ -200,7 +200,7 @@ public class ProjectService { public ProjectVersion getLatestVersion(String projectId) { ProjectVersionExample projectVersionExample = new ProjectVersionExample(); projectVersionExample.createCriteria().andProjectIdEqualTo(projectId); - return projectVersionMapper.selectByExample(projectVersionExample).get(0); + return projectVersionMapper.selectByExample(projectVersionExample).getFirst(); } public Long getNextOrder(Function getLastPosFunc, String projectId) { diff --git a/backend/services/project-management/src/test/java/io/metersphere/project/controller/EnvironmentControllerTests.java b/backend/services/project-management/src/test/java/io/metersphere/project/controller/EnvironmentControllerTests.java index 7b2ff7582c..fadf132017 100644 --- a/backend/services/project-management/src/test/java/io/metersphere/project/controller/EnvironmentControllerTests.java +++ b/backend/services/project-management/src/test/java/io/metersphere/project/controller/EnvironmentControllerTests.java @@ -57,7 +57,6 @@ import org.springframework.util.MultiValueMap; import java.io.File; import java.io.FileInputStream; -import java.io.FileOutputStream; import java.io.IOException; import java.nio.charset.StandardCharsets; import java.util.ArrayList; @@ -786,7 +785,7 @@ public class EnvironmentControllerTests extends BaseTest { EnvironmentExample example = new EnvironmentExample(); example.createCriteria().andProjectIdEqualTo(DEFAULT_PROJECT_ID).andNameEqualTo("name"); List environments = environmentMapper.selectByExample(example); - String id = environments.get(0).getId(); + String id = environments.getFirst().getId(); MvcResult mvcResult = this.responseGet(get + id); EnvironmentInfoDTO response = parseObjectFromMvcResult(mvcResult, EnvironmentInfoDTO.class); Assertions.assertNotNull(response); @@ -817,7 +816,7 @@ public class EnvironmentControllerTests extends BaseTest { EnvironmentExample environmentExample = new EnvironmentExample(); environmentExample.createCriteria().andProjectIdEqualTo(DEFAULT_PROJECT_ID).andMockEqualTo(true); List environmentList = environmentMapper.selectByExample(environmentExample); - MOCKID = environmentList.get(0).getId(); + MOCKID = environmentList.getFirst().getId(); this.responseGet(get + MOCKID); } @@ -858,20 +857,13 @@ public class EnvironmentControllerTests extends BaseTest { dataSource.setDbUrl(null); dataSource.setPassword("Password123@mysql"); dataSource.setUsername("root"); - dataSource.setDriverId(StringUtils.join("system", "&", "com.mysql.cj.jdbc.Driver")); - dataSource.setDriver("com.mysql.cj.jdbc.Driver"); - this.requestPost(validate, dataSource, BAD_REQUEST_MATCHER); //测试mysql DriverId为空 - dataSource.setDbUrl("jdbc:mysql://"); dataSource.setDriverId(null); this.requestPost(validate, dataSource, BAD_REQUEST_MATCHER); - //测试mysql Driver为空 - dataSource.setDriverId(StringUtils.join("system", "&", "com.mysql.cj.jdbc.Driver")); - dataSource.setDriver(null); - this.requestPost(validate, dataSource, BAD_REQUEST_MATCHER); // 测试500 dataSource.setDriver("com.mysql.cj.jdbc.Driver"); dataSource.setDbUrl("jdbc:mysql://"); + dataSource.setDriverId(StringUtils.join("system", "&", "com.mysql.cj.jdbc.Driver")); this.requestPost(validate, dataSource, ERROR_REQUEST_MATCHER); } @@ -893,7 +885,7 @@ public class EnvironmentControllerTests extends BaseTest { EnvironmentExample example = new EnvironmentExample(); example.createCriteria().andProjectIdEqualTo(DEFAULT_PROJECT_ID).andNameEqualTo("name"); List environments = environmentMapper.selectByExample(example); - String id = environments.get(0).getId(); + String id = environments.getFirst().getId(); EnvironmentRequest request = new EnvironmentRequest(); request.setId(id); request.setProjectId(DEFAULT_PROJECT_ID); @@ -912,7 +904,7 @@ public class EnvironmentControllerTests extends BaseTest { example = new EnvironmentExample(); example.createCriteria().andProjectIdEqualTo(DEFAULT_PROJECT_ID).andNameEqualTo("commonParams"); environments = environmentMapper.selectByExample(example); - request.setId(environments.get(0).getId()); + request.setId(environments.getFirst().getId()); request.setName("commonParams"); FileInputStream inputStream = new FileInputStream(new File( this.getClass().getClassLoader().getResource("file/e5d6b195f54c96a59b3c0a9c8888798f.p12") @@ -931,7 +923,7 @@ public class EnvironmentControllerTests extends BaseTest { example = new EnvironmentExample(); example.createCriteria().andProjectIdEqualTo(DEFAULT_PROJECT_ID).andNameEqualTo("校验权限"); environments = environmentMapper.selectByExample(example); - id = environments.get(0).getId(); + id = environments.getFirst().getId(); request.setProjectId(DEFAULT_PROJECT_ID); request.setName("校验更新权限"); request.setId(id); @@ -956,7 +948,7 @@ public class EnvironmentControllerTests extends BaseTest { EnvironmentExample example = new EnvironmentExample(); example.createCriteria().andProjectIdEqualTo(DEFAULT_PROJECT_ID).andNameEqualTo("name"); List environments = environmentMapper.selectByExample(example); - String id = environments.get(0).getId(); + String id = environments.getFirst().getId(); request = new EnvironmentRequest(); request.setId(id); request.setProjectId(DEFAULT_PROJECT_ID); @@ -985,7 +977,7 @@ public class EnvironmentControllerTests extends BaseTest { EnvironmentExample example = new EnvironmentExample(); example.createCriteria().andProjectIdEqualTo(DEFAULT_PROJECT_ID).andMockEqualTo(true); List environments = environmentMapper.selectByExample(example); - posRequest.setMoveId(environments.get(0).getId()); + posRequest.setMoveId(environments.getFirst().getId()); posRequest.setMoveMode("AFTER"); this.requestPostWithOkAndReturn(POS_URL, posRequest, DEFAULT_PROJECT_ID); @@ -1001,7 +993,7 @@ public class EnvironmentControllerTests extends BaseTest { EnvironmentExample example = new EnvironmentExample(); example.createCriteria().andProjectIdEqualTo(DEFAULT_PROJECT_ID).andNameEqualTo("name"); List environments = environmentMapper.selectByExample(example); - String id = environments.get(0).getId(); + String id = environments.getFirst().getId(); this.requestGet(delete + id); //校验日志 checkLog(id, OperationLogType.DELETE); @@ -1017,7 +1009,7 @@ public class EnvironmentControllerTests extends BaseTest { example = new EnvironmentExample(); example.createCriteria().andProjectIdEqualTo(DEFAULT_PROJECT_ID).andNameEqualTo("uploadFile"); environments = environmentMapper.selectByExample(example); - id = environments.get(0).getId(); + id = environments.getFirst().getId(); this.requestGet(delete + id); //查询文件 FileRequest fileRequest = new FileRequest(); @@ -1136,10 +1128,6 @@ public class EnvironmentControllerTests extends BaseTest { request.setExcludeIds(List.of("environmentId1")); MvcResult mvcResult1 = this.requestPostDownloadFile(exportEnv, null, request, DEFAULT_PROJECT_ID); byte[] fileBytes1 = mvcResult1.getResponse().getContentAsByteArray(); - File file = new File("test.json"); - FileOutputStream fileOutputStream = new FileOutputStream(file); - fileOutputStream.write(fileBytes1); - fileOutputStream.close(); Assertions.assertNotNull(fileBytes1); request.setSelectIds(List.of("不存在blob")); request.setSelectAll(false); diff --git a/backend/services/project-management/src/test/java/io/metersphere/project/controller/EnvironmentGroupControllerTests.java b/backend/services/project-management/src/test/java/io/metersphere/project/controller/EnvironmentGroupControllerTests.java index f2d2269105..4f0d25e0d9 100644 --- a/backend/services/project-management/src/test/java/io/metersphere/project/controller/EnvironmentGroupControllerTests.java +++ b/backend/services/project-management/src/test/java/io/metersphere/project/controller/EnvironmentGroupControllerTests.java @@ -213,7 +213,7 @@ public class EnvironmentGroupControllerTests extends BaseTest { } EnvironmentGroupExample example = new EnvironmentGroupExample(); example.createCriteria().andProjectIdEqualTo(DEFAULT_PROJECT_ID).andNameEqualTo("group"); - GROUP_ID = environmentGroupMapper.selectByExample(example).get(0).getId(); + GROUP_ID = environmentGroupMapper.selectByExample(example).getFirst().getId(); return GROUP_ID; } @@ -229,7 +229,7 @@ public class EnvironmentGroupControllerTests extends BaseTest { EnvironmentExample environmentExample = new EnvironmentExample(); environmentExample.createCriteria().andProjectIdEqualTo(DEFAULT_PROJECT_ID).andNameEqualTo("httpConfig-group"); List environments = environmentMapper.selectByExample(environmentExample); - environmentGroupProjectDTO.setEnvironmentId(environments.get(0).getId()); + environmentGroupProjectDTO.setEnvironmentId(environments.getFirst().getId()); environmentGroupProjectDTO.setProjectId(DEFAULT_PROJECT_ID); groupRequest.setEnvGroupProject(List.of(environmentGroupProjectDTO)); MvcResult mvcResult = this.responsePost(update, groupRequest); @@ -306,7 +306,7 @@ public class EnvironmentGroupControllerTests extends BaseTest { EnvironmentGroupExample example = new EnvironmentGroupExample(); example.createCriteria().andProjectIdEqualTo(DEFAULT_PROJECT_ID).andNameEqualTo("校验权限"); List environments = environmentGroupMapper.selectByExample(example); - posRequest.setMoveId(environments.get(0).getId()); + posRequest.setMoveId(environments.getFirst().getId()); posRequest.setMoveMode("AFTER"); this.requestPostWithOkAndReturn(POS_URL, posRequest); diff --git a/backend/services/project-management/src/test/java/io/metersphere/project/controller/GlobalParamsControllerTests.java b/backend/services/project-management/src/test/java/io/metersphere/project/controller/GlobalParamsControllerTests.java index 1bedb52971..11f68eb7ed 100644 --- a/backend/services/project-management/src/test/java/io/metersphere/project/controller/GlobalParamsControllerTests.java +++ b/backend/services/project-management/src/test/java/io/metersphere/project/controller/GlobalParamsControllerTests.java @@ -368,7 +368,7 @@ public class GlobalParamsControllerTests extends BaseTest { List projectParametersList = projectParametersMapper.selectByExample(example); GlobalParamsRequest request = new GlobalParamsRequest(); request.setProjectId("projectId1"); - request.setId(projectParametersList.get(0).getId()); + request.setId(projectParametersList.getFirst().getId()); GlobalParams globalParams = new GlobalParams(); globalParams.setHeaders(getHeaders(2)); globalParams.setCommonVariables(getEnvVariables(2)); @@ -390,7 +390,7 @@ public class GlobalParamsControllerTests extends BaseTest { example.createCriteria().andProjectIdEqualTo(DEFAULT_PROJECT_ID); projectParametersList = projectParametersMapper.selectByExample(example); request.setProjectId(DEFAULT_PROJECT_ID); - request.setId(projectParametersList.get(0).getId()); + request.setId(projectParametersList.getFirst().getId()); GlobalParams globalParams1 = new GlobalParams(); globalParams1.setHeaders(getHeaders(3)); globalParams1.setCommonVariables(getEnvVariables(4)); @@ -575,7 +575,7 @@ public class GlobalParamsControllerTests extends BaseTest { if (CollectionUtils.isEmpty(listObject)) { continue; } - if (listObject.get(0) instanceof File || listObject.get(0) instanceof MockMultipartFile) { + if (listObject.getFirst() instanceof File || listObject.getFirst() instanceof MockMultipartFile) { // 参数是多个文件时,设置多个文件 for (Object subObject : ((List) o)) { multipartFile = getMockMultipartFile(key, subObject); diff --git a/backend/services/system-setting/src/main/java/io/metersphere/system/log/constants/OperationLogModule.java b/backend/services/system-setting/src/main/java/io/metersphere/system/log/constants/OperationLogModule.java index 3d58cd1d50..39160241a2 100644 --- a/backend/services/system-setting/src/main/java/io/metersphere/system/log/constants/OperationLogModule.java +++ b/backend/services/system-setting/src/main/java/io/metersphere/system/log/constants/OperationLogModule.java @@ -22,6 +22,7 @@ public class OperationLogModule { public static final String API_DEFINITION_MOCK = "API_DEFINITION_MOCK"; public static final String API_DEFINITION_CASE = "API_DEFINITION_CASE"; public static final String API_SCENARIO = "API_SCENARIO"; + public static final String API_REPORT = "API_REPORT"; public static final String AUTH_TITLE = "AUTH_TITLE"; public static final String PROJECT_ENVIRONMENT_SETTING = "PROJECT_ENVIRONMENT_SETTING"; public static final String PROJECT_PROJECT_MANAGER = "PROJECT_PROJECT_MANAGER";