diff --git a/backend/framework/sdk/src/main/resources/i18n/project_en_US.properties b/backend/framework/sdk/src/main/resources/i18n/project_en_US.properties index 30f86d5091..0846d412b3 100644 --- a/backend/framework/sdk/src/main/resources/i18n/project_en_US.properties +++ b/backend/framework/sdk/src/main/resources/i18n/project_en_US.properties @@ -91,6 +91,11 @@ functional_case_template_extend.id.not_blank=ID is required functional_case_template_extend.step_model.length_range=Step Model length must be between {min} and {max} functional_case_template_extend.step_model.not_blank=Step Model is required project_is_null=Project does not exist +# robot +robot_is_null=The current robot does not exist +ding_type_is_null= DingTalk robot type is required +ding_app_key_is_null=DingTalk AppKey is required +ding_app_secret_is_null=DingTalk AppSecret is required # permission permission.project_user.name=User permission.project_group.name=User group diff --git a/backend/framework/sdk/src/main/resources/i18n/project_zh_CN.properties b/backend/framework/sdk/src/main/resources/i18n/project_zh_CN.properties index 4d081a5786..0c09c92b5f 100644 --- a/backend/framework/sdk/src/main/resources/i18n/project_zh_CN.properties +++ b/backend/framework/sdk/src/main/resources/i18n/project_zh_CN.properties @@ -91,6 +91,11 @@ functional_case_template_extend.id.not_blank=ID不能为空 functional_case_template_extend.step_model.length_range=步骤模型长度必须在{min}-{max}之间 functional_case_template_extend.step_model.not_blank=步骤模型不能为空 project_not_exist=项目不存在 +# robot +robot_is_null=当前机器人不存在 +ding_type_is_null=钉钉机器人的类型不能为空 +ding_app_key_is_null=钉钉的AppKey不能为空 +ding_app_secret_is_null=钉钉的AppSecret不能为空 # permission permission.project_user.name=用户 permission.project_group.name=用户组 diff --git a/backend/framework/sdk/src/main/resources/i18n/project_zh_TW.properties b/backend/framework/sdk/src/main/resources/i18n/project_zh_TW.properties index aa571d0d8a..683806f662 100644 --- a/backend/framework/sdk/src/main/resources/i18n/project_zh_TW.properties +++ b/backend/framework/sdk/src/main/resources/i18n/project_zh_TW.properties @@ -91,6 +91,11 @@ functional_case_template_extend.id.not_blank=ID不能為空 functional_case_template_extend.step_model.length_range=步驟模型長度必須在{min}-{max}之間 functional_case_template_extend.step_model.not_blank=步驟模型不能為空 project_is_null=項目不存在 +# robot +robot_is_null=當前機器人不存在 +ding_type_is_null=釘釘機器人的類型不能為空 +ding_app_key_is_null = 釘釘的AppKey不能為空 +ding_app_secret_is_null =釘釘的AppSecret不能為空 # permission permission.project_user.name=用戶 permission.project_group.name=用戶組 diff --git a/backend/services/project-management/pom.xml b/backend/services/project-management/pom.xml index 6f75443d93..4c651ed7dd 100644 --- a/backend/services/project-management/pom.xml +++ b/backend/services/project-management/pom.xml @@ -18,7 +18,14 @@ metersphere-sdk ${revision} - + + io.metersphere + metersphere-sdk + ${revision} + tests + test-jar + test + diff --git a/backend/services/project-management/src/main/java/io/metersphere/project/controller/ProjectRobotController.java b/backend/services/project-management/src/main/java/io/metersphere/project/controller/ProjectRobotController.java new file mode 100644 index 0000000000..c00a9d3091 --- /dev/null +++ b/backend/services/project-management/src/main/java/io/metersphere/project/controller/ProjectRobotController.java @@ -0,0 +1,90 @@ +package io.metersphere.project.controller; + +import com.github.pagehelper.Page; +import com.github.pagehelper.PageHelper; +import io.metersphere.project.domain.ProjectRobot; +import io.metersphere.project.dto.ProjectRobotDTO; +import io.metersphere.project.request.ProjectRobotRequest; +import io.metersphere.project.service.ProjectRobotService; +import io.metersphere.sdk.constants.PermissionConstants; +import io.metersphere.sdk.util.BeanUtils; +import io.metersphere.sdk.util.PageUtils; +import io.metersphere.sdk.util.Pager; +import io.metersphere.sdk.util.SessionUtils; +import io.metersphere.validation.groups.Created; +import io.metersphere.validation.groups.Updated; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.annotation.Resource; +import org.apache.shiro.authz.annotation.RequiresPermissions; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import java.util.List; + +@Tag(name = "项目机器人管理") +@RestController +@RequestMapping("/project/robot/") +public class ProjectRobotController { + + @Resource + private ProjectRobotService projectRobotService; + + + @PostMapping("/list/page") + @Operation(summary = "获取机器人列表") + @RequiresPermissions(PermissionConstants.PROJECT_MESSAGE_READ) + public Pager> listResourcePools(@Validated @RequestBody ProjectRobotRequest request) { + Page page = PageHelper.startPage(request.getCurrent(), request.getPageSize(), true); + return PageUtils.setPageInfo(page, projectRobotService.getList(request)); + } + + @PostMapping("add") + @Operation(summary = "新增机器人管理") + @RequiresPermissions(PermissionConstants.PROJECT_MESSAGE_READ_ADD) + public void add(@Validated({Created.class}) @RequestBody ProjectRobotDTO projectRobotDTO) { + ProjectRobot projectRobot = new ProjectRobot(); + BeanUtils.copyBean(projectRobot, projectRobotDTO); + projectRobot.setCreateUser(SessionUtils.getUserId()); + projectRobot.setCreateTime(System.currentTimeMillis()); + projectRobot.setUpdateUser(SessionUtils.getUserId()); + projectRobot.setUpdateTime(System.currentTimeMillis()); + projectRobotService.add(projectRobot); + } + + @PostMapping("update") + @Operation(summary = "更新机器人") + @RequiresPermissions(PermissionConstants.PROJECT_MESSAGE_READ_UPDATE) + public void update(@Validated({Updated.class}) @RequestBody ProjectRobotDTO projectRobotDTO) { + ProjectRobot projectRobot = new ProjectRobot(); + BeanUtils.copyBean(projectRobot, projectRobotDTO); + projectRobot.setCreateUser(null); + projectRobot.setCreateTime(null); + projectRobot.setUpdateUser(SessionUtils.getUserId()); + projectRobot.setUpdateTime(System.currentTimeMillis()); + projectRobotService.update(projectRobot); + } + + @GetMapping("get/{id}") + @Operation(summary = "获取机器人详情") + @RequiresPermissions(PermissionConstants.PROJECT_MESSAGE_READ) + public ProjectRobotDTO getDetail(@PathVariable(value = "id") String id) { + return projectRobotService.getDetail(id); + } + + @GetMapping("delete/{id}") + @Operation(summary = "删除机器人") + @RequiresPermissions(PermissionConstants.PROJECT_MESSAGE_READ_DELETE) + public void delete(@PathVariable(value = "id") String id) { + projectRobotService.delete(id); + } + + @GetMapping("enable/{id}") + @Operation(summary = "禁用机器人") + @RequiresPermissions(PermissionConstants.PROJECT_MESSAGE_READ_UPDATE) + public void enable(@PathVariable(value = "id") String id) { + projectRobotService.enable(id, SessionUtils.getUserId(), System.currentTimeMillis()); + } + + +} diff --git a/backend/services/project-management/src/main/java/io/metersphere/project/dto/ProjectRobotDTO.java b/backend/services/project-management/src/main/java/io/metersphere/project/dto/ProjectRobotDTO.java new file mode 100644 index 0000000000..8ab8546b4f --- /dev/null +++ b/backend/services/project-management/src/main/java/io/metersphere/project/dto/ProjectRobotDTO.java @@ -0,0 +1,38 @@ +package io.metersphere.project.dto; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +@Data +public class ProjectRobotDTO { + + @Schema(description = "id", requiredMode = Schema.RequiredMode.REQUIRED) + private String id; + + @Schema(description = "名称", requiredMode = Schema.RequiredMode.REQUIRED) + private String name; + + @Schema(description = "所属平台(飞书,钉钉,企业微信,自定义)", requiredMode = Schema.RequiredMode.REQUIRED) + private String platform; + + @Schema(description = "webhook", requiredMode = Schema.RequiredMode.REQUIRED) + private String webhook; + + @Schema(description = "钉钉自定义和内部") + private String type; + + @Schema(description = "钉钉AppKey") + private String appKey; + + @Schema(description = "钉钉AppSecret") + private String appSecret; + + @Schema(description = "是否启用") + private Boolean enable; + + @Schema(description = "描述") + private String description; + + @Schema(description = "项目id", requiredMode = Schema.RequiredMode.REQUIRED) + private String projectId; +} diff --git a/backend/services/project-management/src/main/java/io/metersphere/project/enums/ProjectRobotPlatform.java b/backend/services/project-management/src/main/java/io/metersphere/project/enums/ProjectRobotPlatform.java new file mode 100644 index 0000000000..e2375afa78 --- /dev/null +++ b/backend/services/project-management/src/main/java/io/metersphere/project/enums/ProjectRobotPlatform.java @@ -0,0 +1,5 @@ +package io.metersphere.project.enums; + +public enum ProjectRobotPlatform { + DING_TALK, LARK, WE_COM, CUSTOM +} diff --git a/backend/services/project-management/src/main/java/io/metersphere/project/enums/ProjectRobotType.java b/backend/services/project-management/src/main/java/io/metersphere/project/enums/ProjectRobotType.java new file mode 100644 index 0000000000..cf8222acbb --- /dev/null +++ b/backend/services/project-management/src/main/java/io/metersphere/project/enums/ProjectRobotType.java @@ -0,0 +1,5 @@ +package io.metersphere.project.enums; + +public enum ProjectRobotType { + CUSTOM, ENTERPRISE +} diff --git a/backend/services/project-management/src/main/java/io/metersphere/project/request/ProjectRobotRequest.java b/backend/services/project-management/src/main/java/io/metersphere/project/request/ProjectRobotRequest.java new file mode 100644 index 0000000000..d12301940f --- /dev/null +++ b/backend/services/project-management/src/main/java/io/metersphere/project/request/ProjectRobotRequest.java @@ -0,0 +1,11 @@ +package io.metersphere.project.request; + +import io.metersphere.sdk.dto.BasePageRequest; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +@Data +public class ProjectRobotRequest extends BasePageRequest { + @Schema(description = "是否禁用") + private Boolean enable; +} diff --git a/backend/services/project-management/src/main/java/io/metersphere/project/service/ProjectRobotService.java b/backend/services/project-management/src/main/java/io/metersphere/project/service/ProjectRobotService.java new file mode 100644 index 0000000000..7c0ea5421e --- /dev/null +++ b/backend/services/project-management/src/main/java/io/metersphere/project/service/ProjectRobotService.java @@ -0,0 +1,110 @@ +package io.metersphere.project.service; + +import io.metersphere.project.domain.ProjectRobot; +import io.metersphere.project.domain.ProjectRobotExample; +import io.metersphere.project.dto.ProjectRobotDTO; +import io.metersphere.project.enums.ProjectRobotPlatform; +import io.metersphere.project.enums.ProjectRobotType; +import io.metersphere.project.mapper.ProjectRobotMapper; +import io.metersphere.project.request.ProjectRobotRequest; +import io.metersphere.sdk.exception.MSException; +import io.metersphere.sdk.util.BeanUtils; +import io.metersphere.sdk.util.Translator; +import jakarta.annotation.Resource; +import org.apache.commons.lang3.StringUtils; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.List; +import java.util.UUID; + +@Service +@Transactional +public class ProjectRobotService { + + @Resource + private ProjectRobotMapper robotMapper; + + public void add(ProjectRobot projectRobot) { + projectRobot.setId(UUID.randomUUID().toString()); + projectRobot.setEnable(true); + checkDingTalk(projectRobot); + robotMapper.insert(projectRobot); + } + + private static void checkDingTalk(ProjectRobot projectRobot) { + if (StringUtils.equals(projectRobot.getPlatform(), ProjectRobotPlatform.DING_TALK.toString())) { + if (StringUtils.isBlank(projectRobot.getType())) { + throw new MSException(Translator.get("ding_type_is_null")); + } + if (StringUtils.equals(projectRobot.getType(), ProjectRobotType.ENTERPRISE.toString())) { + if (StringUtils.isBlank(projectRobot.getAppKey())) { + throw new MSException(Translator.get("ding_app_key_is_null")); + } + if (StringUtils.isBlank(projectRobot.getAppSecret())) { + throw new MSException(Translator.get("ding_app_secret_is_null")); + } + } + } + } + + public void update(ProjectRobot projectRobot) { + checkRobotExist(projectRobot.getId()); + checkDingTalk(projectRobot); + robotMapper.updateByPrimaryKeySelective(projectRobot); + } + + private ProjectRobot checkRobotExist(String robotId) { + ProjectRobot projectRobotInDB = robotMapper.selectByPrimaryKey(robotId); + if (projectRobotInDB == null) { + throw new MSException(Translator.get("robot_is_null")); + } + return projectRobotInDB; + } + + + public void delete(String id) { + checkRobotExist(id); + robotMapper.deleteByPrimaryKey(id); + } + + public void enable(String id, String updateUser, Long updateTime) { + ProjectRobot projectRobot = checkRobotExist(id); + if (projectRobot.getEnable()) { + projectRobot.setEnable(false); + } else { + projectRobot.setEnable(true); + } + projectRobot.setCreateUser(null); + projectRobot.setCreateTime(null); + projectRobot.setUpdateUser(updateUser); + projectRobot.setUpdateTime(updateTime); + robotMapper.updateByPrimaryKeySelective(projectRobot); + } + + public List getList(ProjectRobotRequest request) { + + ProjectRobotExample projectExample = new ProjectRobotExample(); + ProjectRobotExample.Criteria criteria = projectExample.createCriteria(); + + if (StringUtils.isNotBlank(request.getKeyword())) { + criteria.andNameLike(StringUtils.wrapIfMissing(request.getKeyword(), "%")); + } + + if (request.getEnable() != null) { + criteria.andEnableEqualTo(request.getEnable()); + } + + projectExample.setOrderByClause("create_time desc"); + + return robotMapper.selectByExample(projectExample); + } + + + public ProjectRobotDTO getDetail(String robotId) { + ProjectRobot projectRobotInDB = checkRobotExist(robotId); + ProjectRobotDTO projectRobotDTO = new ProjectRobotDTO(); + BeanUtils.copyBean(projectRobotDTO, projectRobotInDB); + return projectRobotDTO; + } +} diff --git a/backend/services/project-management/src/test/java/io/metersphere/project/controller/ProjectApplicationControllerTests.java b/backend/services/project-management/src/test/java/io/metersphere/project/controller/ProjectApplicationControllerTests.java index f731ca47a9..363152dc78 100644 --- a/backend/services/project-management/src/test/java/io/metersphere/project/controller/ProjectApplicationControllerTests.java +++ b/backend/services/project-management/src/test/java/io/metersphere/project/controller/ProjectApplicationControllerTests.java @@ -1,7 +1,7 @@ package io.metersphere.project.controller; -import com.jayway.jsonpath.JsonPath; import io.metersphere.project.domain.ProjectApplication; +import io.metersphere.sdk.base.BaseTest; import io.metersphere.sdk.constants.SessionConstants; import io.metersphere.sdk.util.JSON; import jakarta.annotation.Resource; @@ -22,28 +22,12 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers. @SpringBootTest @TestMethodOrder(MethodOrderer.OrderAnnotation.class) @AutoConfigureMockMvc -public class ProjectApplicationControllerTests { +public class ProjectApplicationControllerTests extends BaseTest { @Resource private MockMvc mockMvc; - private static String sessionId; - private static String csrfToken; - @Test @Order(0) - public void login() throws Exception { - MvcResult mvcResult = mockMvc.perform(MockMvcRequestBuilders.post("/login") - .content("{\"username\":\"admin\",\"password\":\"metersphere\"}") - .contentType(MediaType.APPLICATION_JSON)) - .andExpect(status().isOk()) - .andExpect(content().contentType(MediaType.APPLICATION_JSON)) - .andReturn(); - sessionId = JsonPath.read(mvcResult.getResponse().getContentAsString(), "$.data.sessionId"); - csrfToken = JsonPath.read(mvcResult.getResponse().getContentAsString(), "$.data.csrfToken"); - } - - @Test - @Order(1) public void testAddApp() throws Exception { ProjectApplication projectApplication = new ProjectApplication(); projectApplication.setProjectId("1"); @@ -60,7 +44,7 @@ public class ProjectApplicationControllerTests { } @Test - @Order(2) + @Order(1) public void testUpdateApp() throws Exception { ProjectApplication projectApplication = new ProjectApplication(); projectApplication.setProjectId("1"); @@ -77,7 +61,7 @@ public class ProjectApplicationControllerTests { } @Test - @Order(3) + @Order(2) public void testListApp() throws Exception { mockMvc.perform(MockMvcRequestBuilders.get("/project/application/list/1") .header(SessionConstants.HEADER_TOKEN, sessionId) diff --git a/backend/services/project-management/src/test/java/io/metersphere/project/controller/ProjectControllerTests.java b/backend/services/project-management/src/test/java/io/metersphere/project/controller/ProjectControllerTests.java index f601e7cc30..51f5777e1b 100644 --- a/backend/services/project-management/src/test/java/io/metersphere/project/controller/ProjectControllerTests.java +++ b/backend/services/project-management/src/test/java/io/metersphere/project/controller/ProjectControllerTests.java @@ -1,6 +1,7 @@ package io.metersphere.project.controller; import com.jayway.jsonpath.JsonPath; +import io.metersphere.sdk.base.BaseTest; import jakarta.annotation.Resource; import org.junit.jupiter.api.MethodOrderer; import org.junit.jupiter.api.Order; @@ -20,28 +21,10 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers. @SpringBootTest @AutoConfigureMockMvc @TestMethodOrder(MethodOrderer.OrderAnnotation.class) -public class ProjectControllerTests { +public class ProjectControllerTests extends BaseTest { @Resource private MockMvc mockMvc; - private static String projectId; - private static String sessionId; - private static String csrfToken; - - - @Test - @Order(0) - public void login() throws Exception { - MvcResult mvcResult = mockMvc.perform(MockMvcRequestBuilders.post("/login") - .content("{\"username\":\"admin\",\"password\":\"metersphere\"}") - .contentType(MediaType.APPLICATION_JSON)) - .andExpect(status().isOk()) - .andExpect(content().contentType(MediaType.APPLICATION_JSON)) - .andReturn(); - sessionId = JsonPath.read(mvcResult.getResponse().getContentAsString(), "$.data.sessionId"); - csrfToken = JsonPath.read(mvcResult.getResponse().getContentAsString(), "$.data.csrfToken"); - } - // 添加项目 // @Test // @Order(1) diff --git a/backend/services/project-management/src/test/java/io/metersphere/project/controller/ProjectRobotControllerTests.java b/backend/services/project-management/src/test/java/io/metersphere/project/controller/ProjectRobotControllerTests.java new file mode 100644 index 0000000000..8a92b65d6f --- /dev/null +++ b/backend/services/project-management/src/test/java/io/metersphere/project/controller/ProjectRobotControllerTests.java @@ -0,0 +1,389 @@ +package io.metersphere.project.controller; + +import io.metersphere.project.domain.ProjectRobot; +import io.metersphere.project.dto.ProjectRobotDTO; +import io.metersphere.project.enums.ProjectRobotPlatform; +import io.metersphere.project.enums.ProjectRobotType; +import io.metersphere.project.request.ProjectRobotRequest; +import io.metersphere.sdk.base.BaseTest; +import io.metersphere.sdk.constants.SessionConstants; +import io.metersphere.sdk.controller.handler.ResultHolder; + +import io.metersphere.sdk.util.BeanUtils; +import io.metersphere.sdk.util.JSON; +import io.metersphere.sdk.util.Pager; + +import org.apache.commons.lang3.StringUtils; +import org.junit.jupiter.api.*; +import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.http.MediaType; +import org.springframework.test.web.servlet.MvcResult; +import org.springframework.test.web.servlet.ResultMatcher; +import org.springframework.test.web.servlet.request.MockMvcRequestBuilders; + +import java.io.UnsupportedEncodingException; +import java.nio.charset.StandardCharsets; +import java.util.List; + +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + +@SpringBootTest +@AutoConfigureMockMvc +@TestMethodOrder(MethodOrderer.OrderAnnotation.class) +public class ProjectRobotControllerTests extends BaseTest { + + public static final String ROBOT_ADD = "/project/robot/add"; + + public static final String ROBOT_UPDATE = "/project/robot/update"; + + public static final String ROBOT_DELETE = "/project/robot/delete"; + + public static final String ROBOT_ENABLE = "/project/robot/enable"; + + public static final String ROBOT_LIST = "/project/robot/list/page"; + + public static final String ROBOT_DETAIL = "/project/robot/get"; + + + @Test + @Order(1) + void addRobotSuccessWeCom() throws Exception { + ProjectRobotDTO projectRobotDTO = new ProjectRobotDTO(); + projectRobotDTO.setName("企业微信机器人"); + projectRobotDTO.setPlatform(ProjectRobotPlatform.WE_COM.toString()); + projectRobotDTO.setProjectId("test_project"); + projectRobotDTO.setWebhook("https://qyapi.weixin.qq.com/cgi-bin/webhook/send?key=2b67ccf4-e0da-4cd6-ae74-8d42657865f8"); + getPostResult(projectRobotDTO, ROBOT_ADD, status().isOk()); + listByKeyWord("企业微信机器人"); + + } + + @Test + @Order(2) + void addRobotSuccessLark() throws Exception { + ProjectRobotDTO projectRobotDTO = new ProjectRobotDTO(); + projectRobotDTO.setName("飞书机器人"); + projectRobotDTO.setPlatform(ProjectRobotPlatform.LARK.toString()); + projectRobotDTO.setProjectId("test_project"); + projectRobotDTO.setWebhook("https://open.feishu.cn/open-apis/bot/v2/hook/a6024229-9d9d-41c2-8662-7bc3da1092cb"); + getPostResult(projectRobotDTO, ROBOT_ADD, status().isOk()); + listByKeyWord("飞书机器人"); + } + + @Test + @Order(3) + void addRobotSuccessDingCustom() throws Exception { + setDingCustom("钉钉自定义机器人"); + listByKeyWord("钉钉自定义机器人"); + } + + private void setDingCustom(String name) throws Exception { + ProjectRobotDTO projectRobotDTO = new ProjectRobotDTO(); + projectRobotDTO.setName(name); + projectRobotDTO.setPlatform(ProjectRobotPlatform.DING_TALK.toString()); + projectRobotDTO.setProjectId("test_project"); + projectRobotDTO.setType(ProjectRobotType.CUSTOM.toString()); + projectRobotDTO.setWebhook("https://oapi.dingtalk.com/robot/send?access_token=fd963136a4d7eebaaa68de261223089148e62d7519fbaf426626fe3157725b8a"); + getPostResult(projectRobotDTO, ROBOT_ADD, status().isOk()); + } + + @Test + @Order(4) + void addRobotSuccessDingEn() throws Exception { + setDingEn("钉钉企业应用机器人"); + listByKeyWord("钉钉企业应用机器人"); + } + + private void setDingEn(String name) throws Exception { + ProjectRobotDTO projectRobotDTO = new ProjectRobotDTO(); + projectRobotDTO.setName(name); + projectRobotDTO.setPlatform(ProjectRobotPlatform.DING_TALK.toString()); + projectRobotDTO.setProjectId("test_project"); + projectRobotDTO.setType(ProjectRobotType.ENTERPRISE.toString()); + projectRobotDTO.setAppKey("dingxwd71o7kj4qoixo7"); + projectRobotDTO.setAppSecret("szmOD9bjGgKtfYk09-Xx2rPdX-xkW4R8Iic0eig_k1D3k95nG4TLKRSpUKUD_f0G"); + projectRobotDTO.setWebhook("https://oapi.dingtalk.com/robot/send?access_token=e971f376669334cd44c585d419f0fdfa1600f97f906109b377999d8a0986b11e"); + getPostResult(projectRobotDTO, ROBOT_ADD, status().isOk()); + } + + @Test + @Order(5) + void addRobotFailDingCustomByType() throws Exception { + ProjectRobotDTO projectRobotDTO = new ProjectRobotDTO(); + projectRobotDTO.setName("钉钉自定义机器人"); + projectRobotDTO.setPlatform(ProjectRobotPlatform.DING_TALK.toString()); + projectRobotDTO.setProjectId("test_project"); + projectRobotDTO.setWebhook("https://oapi.dingtalk.com/robot/send?access_token=e971f376669334cd44c585d419f0fdfa1600f97f906109b377999d8a0986b11e"); + getPostResult(projectRobotDTO, ROBOT_ADD, status().is5xxServerError()); + } + + @Test + @Order(6) + void addRobotFailDingCustomByKey() throws Exception { + ProjectRobotDTO projectRobotDTO = new ProjectRobotDTO(); + projectRobotDTO.setName("钉钉自定义机器人"); + projectRobotDTO.setPlatform(ProjectRobotPlatform.DING_TALK.toString()); + projectRobotDTO.setProjectId("test_project"); + projectRobotDTO.setType(ProjectRobotType.ENTERPRISE.toString()); + projectRobotDTO.setWebhook("https://oapi.dingtalk.com/robot/send?access_token=e971f376669334cd44c585d419f0fdfa1600f97f906109b377999d8a0986b11e"); + getPostResult(projectRobotDTO, ROBOT_ADD, status().is5xxServerError()); + } + + @Test + @Order(7) + void addRobotFailDingCustomBySecret() throws Exception { + ProjectRobotDTO projectRobotDTO = new ProjectRobotDTO(); + projectRobotDTO.setName("钉钉自定义机器人"); + projectRobotDTO.setPlatform(ProjectRobotPlatform.DING_TALK.toString()); + projectRobotDTO.setProjectId("test_project"); + projectRobotDTO.setType(ProjectRobotType.ENTERPRISE.toString()); + projectRobotDTO.setAppKey("dingxwd71o7kj4qoixo7"); + projectRobotDTO.setWebhook("https://oapi.dingtalk.com/robot/send?access_token=e971f376669334cd44c585d419f0fdfa1600f97f906109b377999d8a0986b11e"); + getPostResult(projectRobotDTO, ROBOT_ADD, status().is5xxServerError()); + } + + @Test + @Order(8) + void updateRobotSuccessCusTom() throws Exception { + setCustomRobot("用于更新自定义机器人"); + ProjectRobot projectRobot = getRobot("用于更新自定义机器人"); + checkUpdate(projectRobot, "更新自定义机器人", status().isOk()); + } + + @Test + @Order(9) + void updateRobotSuccessDingCus() throws Exception { + setDingCustom("用于更新钉钉自定义机器人"); + ProjectRobot projectRobot = getRobot("用于更新钉钉自定义机器人"); + checkUpdate(projectRobot, "更新钉钉自定义机器人", status().isOk()); + } + + @Test + @Order(10) + void updateRobotSuccessDingEn() throws Exception { + setDingEn("用于更新钉钉企业机器人"); + ProjectRobot projectRobot = getRobot("用于更新钉钉企业机器人"); + checkUpdate(projectRobot, "更新钉钉企业机器人", status().isOk()); + } + + @Test + @Order(11) + void updateRobotFileIdNotExist() throws Exception { + setCustomRobot("测试没有ID失败"); + ProjectRobot projectRobot = getRobot("测试没有ID失败"); + projectRobot.setId("noId"); + checkUpdate(projectRobot, "测试没有ID失败", status().is5xxServerError()); + } + + @Test + @Order(12) + void updateRobotFileIdNoId() throws Exception { + setCustomRobot("测试ID空失败"); + ProjectRobot projectRobot = getRobot("测试ID空失败"); + projectRobot.setId(null); + checkUpdate(projectRobot, "测试ID空失败", status().is5xxServerError()); + } + + @Test + @Order(13) + void updateRobotFileNoDingType() throws Exception { + setDingCustom("测试更新没有Type失败"); + ProjectRobot projectRobot = getRobot("测试更新没有Type失败"); + projectRobot.setType(null); + checkUpdate(projectRobot, "测试更新没有Type失败", status().is5xxServerError()); + } + + @Test + @Order(14) + void updateRobotFileNoDingKey() throws Exception { + setDingEn("测试更新没有key失败"); + ProjectRobot projectRobot = getRobot("测试更新没有key失败"); + projectRobot.setAppKey(null); + checkUpdate(projectRobot, "测试更新没有key失败", status().is5xxServerError()); + } + + @Test + @Order(15) + void updateRobotFileNoDingSecret() throws Exception { + setDingEn("测试更新没有Secret失败"); + ProjectRobot projectRobot = getRobot("测试更新没有Secret失败"); + projectRobot.setAppSecret(null); + checkUpdate(projectRobot, "测试更新没有Secret失败", status().is5xxServerError()); + } + + @Test + @Order(16) + void deleteRobotSuccess() throws Exception { + setCustomRobot("测试删除"); + ProjectRobot projectRobot = getRobot("测试删除"); + String projectRobotId = projectRobot.getId(); + mockMvc.perform(MockMvcRequestBuilders.get(ROBOT_DELETE+"/"+ projectRobotId) + .header(SessionConstants.HEADER_TOKEN, sessionId) + .header(SessionConstants.CSRF_TOKEN, csrfToken)) + .andExpect(status().isOk()) + .andExpect(content().contentType(MediaType.APPLICATION_JSON)); + mockMvc.perform(MockMvcRequestBuilders.get(ROBOT_DETAIL + "/" + projectRobotId) + .header(SessionConstants.HEADER_TOKEN, sessionId) + .header(SessionConstants.CSRF_TOKEN, csrfToken)) + .andExpect(status().is5xxServerError()) + .andExpect(content().contentType(MediaType.APPLICATION_JSON)).andReturn(); + } + + + @Test + @Order(17) + void deleteRobotFail() throws Exception { + mockMvc.perform(MockMvcRequestBuilders.get(ROBOT_DELETE+"/no_id") + .header(SessionConstants.HEADER_TOKEN, sessionId) + .header(SessionConstants.CSRF_TOKEN, csrfToken)) + .andExpect(status().is5xxServerError()) + .andExpect(content().contentType(MediaType.APPLICATION_JSON)); + } + + @Test + @Order(18) + void getDetailFail() throws Exception { + mockMvc.perform(MockMvcRequestBuilders.get(ROBOT_DETAIL+"/no_id") + .header(SessionConstants.HEADER_TOKEN, sessionId) + .header(SessionConstants.CSRF_TOKEN, csrfToken)) + .andExpect(status().is5xxServerError()) + .andExpect(content().contentType(MediaType.APPLICATION_JSON)); + } + + @Test + @Order(16) + void getDetailSuccess() throws Exception { + setCustomRobot("测试获取详情"); + ProjectRobot projectRobot = getRobot("测试获取详情"); + MvcResult mvcResult = mockMvc.perform(MockMvcRequestBuilders.get(ROBOT_DETAIL + "/" + projectRobot.getId()) + .header(SessionConstants.HEADER_TOKEN, sessionId) + .header(SessionConstants.CSRF_TOKEN, csrfToken)) + .andExpect(status().isOk()) + .andExpect(content().contentType(MediaType.APPLICATION_JSON)).andReturn(); + ProjectRobot result = getResult(mvcResult); + Assertions.assertTrue(StringUtils.equals(result.getName(), "测试获取详情")); + Assertions.assertTrue(StringUtils.equals(result.getWebhook(),"https://qyapi.weixin.qq.com/cgi-bin/webhook/send?key=2b67ccf4-e0da-4cd6-ae74-8d42657865f8")); + } + + @Test + @Order(17) + void getListSuccessNoKeyword() throws Exception { + ProjectRobotRequest request = new ProjectRobotRequest(); + request.setCurrent(1); + request.setPageSize(5); + Pager sortPageData = getPager(request); + List projectRobots = JSON.parseArray(JSON.toJSONString(sortPageData.getList()), ProjectRobot.class); + Assertions.assertTrue(projectRobots.size()>0); + } + + @Test + @Order(18) + void getListSuccessEnable() throws Exception { + setCustomRobot("测试集合"); + ProjectRobotRequest request = new ProjectRobotRequest(); + request.setCurrent(1); + request.setPageSize(5); + request.setKeyword("测试集合"); + request.setEnable(true); + Pager sortPageData = getPager(request); + List projectRobots = JSON.parseArray(JSON.toJSONString(sortPageData.getList()), ProjectRobot.class); + ProjectRobot projectRobot = projectRobots.get(0); + Assertions.assertTrue(projectRobot.getEnable()); + } + + @Test + @Order(19) + void setEnableSuccess() throws Exception { + setCustomRobot("测试Enable"); + ProjectRobot projectRobot = getRobot("测试Enable"); + String projectRobotId = projectRobot.getId(); + mockMvc.perform(MockMvcRequestBuilders.get(ROBOT_ENABLE+"/"+ projectRobotId) + .header(SessionConstants.HEADER_TOKEN, sessionId) + .header(SessionConstants.CSRF_TOKEN, csrfToken)) + .andExpect(status().isOk()) + .andExpect(content().contentType(MediaType.APPLICATION_JSON)); + ProjectRobot projectRobotEnable = getRobot("测试Enable"); + Assertions.assertFalse(projectRobotEnable.getEnable()); + } + + @Test + @Order(20) + void setEnableFail() throws Exception { + mockMvc.perform(MockMvcRequestBuilders.get(ROBOT_ENABLE+"/no_id") + .header(SessionConstants.HEADER_TOKEN, sessionId) + .header(SessionConstants.CSRF_TOKEN, csrfToken)) + .andExpect(status().is5xxServerError()) + .andExpect(content().contentType(MediaType.APPLICATION_JSON)); + + } + + private static ProjectRobot getResult(MvcResult mvcResult) throws UnsupportedEncodingException { + String contentAsString = mvcResult.getResponse().getContentAsString(StandardCharsets.UTF_8);; + ResultHolder resultHolder = JSON.parseObject(contentAsString, ResultHolder.class); + return JSON.parseObject(JSON.toJSONString(resultHolder.getData()), ProjectRobot.class); + } + + + private void checkUpdate(ProjectRobot projectRobot, String name, ResultMatcher resultMatcher) throws Exception { + projectRobot.setName(name); + ProjectRobotDTO projectRobotDTO = new ProjectRobotDTO(); + BeanUtils.copyBean(projectRobotDTO, projectRobot); + getPostResult(projectRobotDTO, ROBOT_UPDATE, resultMatcher); + if (resultMatcher.equals(status().isOk())) { + ProjectRobot projectRobotUpdate = getRobot(name); + Assertions.assertTrue(StringUtils.equals(projectRobotUpdate.getName(), name)); + } + } + + private void setCustomRobot(String name) throws Exception { + ProjectRobotDTO projectRobotDTO = new ProjectRobotDTO(); + projectRobotDTO.setName(name); + projectRobotDTO.setPlatform(ProjectRobotPlatform.CUSTOM.toString()); + projectRobotDTO.setProjectId("test_project"); + projectRobotDTO.setWebhook("https://qyapi.weixin.qq.com/cgi-bin/webhook/send?key=2b67ccf4-e0da-4cd6-ae74-8d42657865f8"); + getPostResult(projectRobotDTO, ROBOT_ADD, status().isOk()); + } + + private void getPostResult(ProjectRobotDTO projectRobotDTO, String url, ResultMatcher resultMatcher) throws Exception { + mockMvc.perform(MockMvcRequestBuilders.post(url) + .header(SessionConstants.HEADER_TOKEN, sessionId) + .header(SessionConstants.CSRF_TOKEN, csrfToken) + .content(JSON.toJSONString(projectRobotDTO)) + .contentType(MediaType.APPLICATION_JSON)) + .andExpect(resultMatcher) + .andExpect(content().contentType(MediaType.APPLICATION_JSON)).andReturn(); + } + + void listByKeyWord(String keyWord) throws Exception { + ProjectRobot projectRobot = getRobot(keyWord); + Assertions.assertTrue(StringUtils.equals(projectRobot.getName(), keyWord)); + } + + private ProjectRobot getRobot(String keyWord) throws Exception { + ProjectRobotRequest request = new ProjectRobotRequest(); + request.setCurrent(1); + request.setPageSize(5); + request.setKeyword(keyWord); + Pager sortPageData = getPager(request); + ProjectRobot projectRobot = JSON.parseArray(JSON.toJSONString(sortPageData.getList()), ProjectRobot.class).get(0); + return projectRobot; + } + + private Pager getPager(ProjectRobotRequest request) throws Exception { + MvcResult mvcResult = mockMvc.perform(MockMvcRequestBuilders.post(ROBOT_LIST) + .header(SessionConstants.HEADER_TOKEN, sessionId) + .header(SessionConstants.CSRF_TOKEN, csrfToken) + .content(JSON.toJSONString(request)) + .contentType(MediaType.APPLICATION_JSON)) + .andExpect(status().isOk()) + .andExpect(content().contentType(MediaType.APPLICATION_JSON)).andReturn(); + String sortData = mvcResult.getResponse().getContentAsString(StandardCharsets.UTF_8); + ResultHolder sortHolder = JSON.parseObject(sortData, ResultHolder.class); + Pager sortPageData = JSON.parseObject(JSON.toJSONString(sortHolder.getData()), Pager.class); + return sortPageData; + } + + +} diff --git a/backend/services/project-management/src/test/resources/application.properties b/backend/services/project-management/src/test/resources/application.properties index 6bec17e5bb..637eab5e89 100644 --- a/backend/services/project-management/src/test/resources/application.properties +++ b/backend/services/project-management/src/test/resources/application.properties @@ -26,7 +26,10 @@ spring.datasource.hikari.max-lifetime=1800000 spring.datasource.hikari.connection-timeout=30000 spring.datasource.hikari.connection-test-query=SELECT 1 -# +# 单元测试初始化权限 sql +spring.sql.init.mode=always +spring.sql.init.schema-locations=classpath*:dml/init_permission_test.sql + # spring.kafka spring.kafka.bootstrap-servers=${embedded.kafka.brokerList} spring.kafka.consumer.group-id=metersphere_group_id diff --git a/backend/services/project-management/src/test/resources/dml/init_permission_test.sql b/backend/services/project-management/src/test/resources/dml/init_permission_test.sql new file mode 100644 index 0000000000..22ec81048c --- /dev/null +++ b/backend/services/project-management/src/test/resources/dml/init_permission_test.sql @@ -0,0 +1,29 @@ +-- 初始化用于权限测试的用户 +INSERT INTO user(id, name, email, password, create_time, update_time, language, last_organization_id, phone, source, + last_project_id, create_user, update_user, deleted) +VALUES ('SYSTEM', 'SYSTEM', 'SYSTEM@fit2cloud.com', MD5('metersphere'), + UNIX_TIMESTAMP() * 1000, + UNIX_TIMESTAMP() * 1000, NULL, NUll, '', 'LOCAL', NULL, 'admin', 'admin', false); + +-- 初始化一个用于权限测试的用户组,这里默认使用 SYSTEM 作为ID,如果是组织和项目级别类似,便于根据权限的前缀找到对应测试的用户组 +INSERT INTO user_role (id, name, description, internal, type, create_time, update_time, create_user, scope_id) +VALUES ('SYSTEM', '系统级别权限校验', '', 1, 'SYSTEM', 1620674220005, 1620674220000, 'admin', 'global'); + +-- 初始化用户和组的关系 +INSERT INTO user_role_relation (id, user_id, role_id, source_id, create_time, create_user) +VALUES ('SYSTEM', 'SYSTEM', 'SYSTEM', 'system', 1684747668375, 'admin'); + +-- 初始化用于权限测试的组织用户 +INSERT INTO user(id, name, email, password, create_time, update_time, language, last_organization_id, phone, source, + last_project_id, create_user, update_user, deleted) +VALUES ('ORGANIZATION', 'ORGANIZATION', 'ORGANIZATION@fit2cloud.com', MD5('metersphere'), + UNIX_TIMESTAMP() * 1000, + UNIX_TIMESTAMP() * 1000, NULL, NUll, '', 'LOCAL', NULL, 'admin', 'admin', false); + +-- 初始化一个用于权限测试的用户组,这里默认使用 ORGANIZATION 作为ID,如果是组织和项目级别类似,便于根据权限的前缀找到对应测试的用户组 +INSERT INTO user_role (id, name, description, internal, type, create_time, update_time, create_user, scope_id) +VALUES ('ORGANIZATION', '组织级别权限校验', '', 1, 'ORGANIZATION', 1620674220005, 1620674220000, 'admin', 'global'); + +-- 初始化用户和组的关系 +INSERT INTO user_role_relation (id, user_id, role_id, source_id, create_time, create_user) +SELECT 'ORGANIZATION', 'ORGANIZATION', 'ORGANIZATION', id, 1684747668375, 'admin' FROM organization WHERE num = 100001; \ No newline at end of file