diff --git a/backend/app/src/main/resources/migration/3.0.0/ddl/V3.0.0_5__api_ddl.sql b/backend/app/src/main/resources/migration/3.0.0/ddl/V3.0.0_5__api_ddl.sql index bda98b50e6..9430cb4632 100644 --- a/backend/app/src/main/resources/migration/3.0.0/ddl/V3.0.0_5__api_ddl.sql +++ b/backend/app/src/main/resources/migration/3.0.0/ddl/V3.0.0_5__api_ddl.sql @@ -1,5 +1,61 @@ -- set innodb lock wait timeout SET SESSION innodb_lock_wait_timeout = 7200; +DROP TABLE IF EXISTS api_definition; +CREATE TABLE api_definition( + `id` VARCHAR(50) NOT NULL COMMENT '接口pk' , + `create_time` BIGINT NOT NULL COMMENT '创建时间' , + `create_user` VARCHAR(50) NOT NULL COMMENT '创建人' , + `update_time` BIGINT NOT NULL COMMENT '修改时间' , + `update_user` VARCHAR(50) NOT NULL COMMENT '修改人' , + `delete_user` VARCHAR(50) COMMENT '删除人' , + `delete_time` BIGINT COMMENT '删除时间' , + `deleted` BIT(1) NOT NULL DEFAULT 0 COMMENT '删除状态' , + `name` VARCHAR(255) NOT NULL COMMENT '接口名称' , + `method` VARCHAR(50) NOT NULL COMMENT '接口类型' , + `protocol` VARCHAR(20) NOT NULL COMMENT '接口协议' , + `path` VARCHAR(255) DEFAULT '' COMMENT '接口路径-只有HTTP协议有值' , + `module_path` VARCHAR(1000) COMMENT '模块全路径-用于导入处理' , + `status` VARCHAR(50) NOT NULL COMMENT '接口状态/进行中/已完成' , + `module_id` VARCHAR(50) DEFAULT 'NONE' COMMENT '模块fk' , + `num` INT COMMENT '自定义id' , + `tags` VARCHAR(1000) COMMENT '标签' , + `pos` BIGINT NOT NULL COMMENT '自定义排序' , + `sync_enable` BIT(1) NOT NULL DEFAULT 0 COMMENT '是否启用同步' , + `sync_time` BIGINT COMMENT '同步开始时间' , + `project_id` VARCHAR(50) NOT NULL COMMENT '项目fk' , + `environment_id` VARCHAR(50) COMMENT '环境fk' , + `latest` BIT(1) NOT NULL DEFAULT 0 COMMENT '是否为最新版本 0:否,1:是' , + `version_id` VARCHAR(50) COMMENT '版本fk' , + `ref_id` VARCHAR(50) COMMENT '版本引用fk' , + `description` VARCHAR(500) COMMENT '描述' , + PRIMARY KEY (id) +) COMMENT = '接口定义'; + + +CREATE INDEX idx_project_id ON api_definition(project_id); +CREATE INDEX idx_module_id ON api_definition(module_id); +CREATE INDEX idx_ref_id ON api_definition(ref_id); +CREATE INDEX idx_version_id ON api_definition(version_id); +CREATE INDEX idx_method ON api_definition(method); +CREATE INDEX idx_status ON api_definition(status); +CREATE INDEX idx_pos ON api_definition(pos); +CREATE INDEX idx_protocol ON api_definition(protocol); +CREATE INDEX idx_create_time ON api_definition(create_time); +CREATE INDEX idx_create_user ON api_definition(create_user); +CREATE INDEX idx_name ON api_definition(name); +CREATE INDEX idx_path ON api_definition(path); + +DROP TABLE IF EXISTS api_definition_blob; +CREATE TABLE api_definition_blob( + `id` VARCHAR(50) NOT NULL COMMENT '接口fk/ 一对一关系' , + `request` LONGBLOB COMMENT '请求内容' , + `response` LONGBLOB COMMENT '响应内容' , + `remark` BLOB COMMENT '备注' , + PRIMARY KEY (id) +) COMMENT = '接口定义详情内容'; + + + -- set innodb lock wait timeout to default SET SESSION innodb_lock_wait_timeout = DEFAULT; \ No newline at end of file diff --git a/backend/framework/sdk/src/main/java/io/metersphere/sdk/util/PageUtils.java b/backend/framework/sdk/src/main/java/io/metersphere/sdk/util/PageUtils.java new file mode 100644 index 0000000000..d5778a5716 --- /dev/null +++ b/backend/framework/sdk/src/main/java/io/metersphere/sdk/util/PageUtils.java @@ -0,0 +1,18 @@ +package io.metersphere.sdk.util; + +import com.github.pagehelper.Page; + +public class PageUtils { + public static Pager setPageInfo(Page page, T list) { + try { + Pager pager = new Pager<>(); + pager.setList(list); + pager.setPageSize(page.getPages()); + pager.setCurrent(page.getPageNum()); + pager.setTotal(page.getTotal()); + return pager; + } catch (Exception e) { + throw new RuntimeException("Error saving current page number data!"); + } + } +} diff --git a/backend/framework/sdk/src/main/java/io/metersphere/sdk/util/Pager.java b/backend/framework/sdk/src/main/java/io/metersphere/sdk/util/Pager.java new file mode 100644 index 0000000000..d63185e1c3 --- /dev/null +++ b/backend/framework/sdk/src/main/java/io/metersphere/sdk/util/Pager.java @@ -0,0 +1,21 @@ +package io.metersphere.sdk.util; + +import lombok.Data; + +@Data +public class Pager { + private T list; + private long total; + private long pageSize; + private long current; + + public Pager() { + } + + public Pager(T list, long total, long pageSize, long current) { + this.list = list; + this.total = total; + this.pageSize = pageSize; + this.current = current; + } +} diff --git a/backend/services/api-test/src/main/java/io/metersphere/api/controller/ApiDefinitionController.java b/backend/services/api-test/src/main/java/io/metersphere/api/controller/ApiDefinitionController.java index b29605e905..132a8419e2 100644 --- a/backend/services/api-test/src/main/java/io/metersphere/api/controller/ApiDefinitionController.java +++ b/backend/services/api-test/src/main/java/io/metersphere/api/controller/ApiDefinitionController.java @@ -1,9 +1,16 @@ package io.metersphere.api.controller; +import com.github.pagehelper.Page; +import com.github.pagehelper.PageHelper; +import io.metersphere.api.domain.ApiDefinition; import io.metersphere.api.dto.ApiDefinitionDTO; +import io.metersphere.api.dto.ApiDefinitionListRequest; import io.metersphere.api.service.ApiDefinitionService; +import io.metersphere.sdk.util.PageUtils; +import io.metersphere.sdk.util.Pager; import io.metersphere.validation.groups.Created; import jakarta.annotation.Resource; +import org.apache.commons.lang3.StringUtils; import org.springframework.http.MediaType; import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.*; @@ -23,4 +30,12 @@ public class ApiDefinitionController { @RequestParam(value = "files") List bodyFiles) { return apiDefinitionService.create(request, bodyFiles); } + + @PostMapping(value = "/page") + public Pager> list(@Validated @RequestBody ApiDefinitionListRequest request) { + Page page = PageHelper.startPage(request.getCurrent(), request.getPageSize(), + StringUtils.isNotBlank(request.getOrderColumn()) ? request.getOrderColumn() : "create_time desc"); + return PageUtils.setPageInfo(page, apiDefinitionService.list(request)); + } + } diff --git a/backend/services/api-test/src/main/java/io/metersphere/api/dto/ApiDefinitionListRequest.java b/backend/services/api-test/src/main/java/io/metersphere/api/dto/ApiDefinitionListRequest.java new file mode 100644 index 0000000000..7a681ad330 --- /dev/null +++ b/backend/services/api-test/src/main/java/io/metersphere/api/dto/ApiDefinitionListRequest.java @@ -0,0 +1,24 @@ +package io.metersphere.api.dto; + +import jakarta.validation.constraints.Min; +import jakarta.validation.constraints.NotBlank; +import lombok.Data; + + +@Data +public class ApiDefinitionListRequest { + private String name; + private String path; + private String method; + private String protocol; + + @NotBlank + private String projectId; + + private String OrderColumn; + + @Min(value = 1, message = "当前页码必须大于0") + private int current; + @Min(value = 5, message = "每页显示条数必须不小于5") + private int pageSize; +} diff --git a/backend/services/api-test/src/main/java/io/metersphere/api/service/ApiDefinitionService.java b/backend/services/api-test/src/main/java/io/metersphere/api/service/ApiDefinitionService.java index 0acf0f3f59..8ac1df44ae 100644 --- a/backend/services/api-test/src/main/java/io/metersphere/api/service/ApiDefinitionService.java +++ b/backend/services/api-test/src/main/java/io/metersphere/api/service/ApiDefinitionService.java @@ -1,6 +1,13 @@ package io.metersphere.api.service; +import io.metersphere.api.domain.ApiDefinition; +import io.metersphere.api.domain.ApiDefinitionExample; import io.metersphere.api.dto.ApiDefinitionDTO; +import io.metersphere.api.dto.ApiDefinitionListRequest; +import io.metersphere.api.mapper.ApiDefinitionMapper; +import jakarta.annotation.Resource; +import jakarta.validation.constraints.NotNull; +import org.apache.commons.lang3.StringUtils; import org.springframework.stereotype.Service; import org.springframework.web.multipart.MultipartFile; @@ -9,7 +16,34 @@ import java.util.List; @Service public class ApiDefinitionService { + @Resource + private ApiDefinitionMapper apiDefinitionMapper; + public ApiDefinitionDTO create(ApiDefinitionDTO request, List bodyFiles) { return request; } + + + public List list(@NotNull ApiDefinitionListRequest request) { + ApiDefinitionExample example = new ApiDefinitionExample(); + ApiDefinitionExample.Criteria criteria = example.createCriteria(); + criteria.andProjectIdEqualTo(request.getProjectId()); + if (StringUtils.isNotBlank(request.getName())) { + criteria.andNameLike("%" + request.getName() + "%"); + } + + if (StringUtils.isNotEmpty(request.getPath())) { + criteria.andPathEqualTo(request.getPath()); + } + + if (StringUtils.isNotEmpty(request.getMethod())) { + criteria.andMethodEqualTo(request.getMethod()); + } + if (StringUtils.isNotBlank(request.getProtocol())) { + criteria.andProtocolEqualTo(request.getProtocol()); + } + + + return apiDefinitionMapper.selectByExample(example); + } } diff --git a/backend/services/api-test/src/main/test/java/io/metersphere/api/controller/ApiDefinitionControllerTests.java b/backend/services/api-test/src/main/test/java/io/metersphere/api/controller/ApiDefinitionControllerTests.java index 9c3f286afa..6d3d6dae9d 100644 --- a/backend/services/api-test/src/main/test/java/io/metersphere/api/controller/ApiDefinitionControllerTests.java +++ b/backend/services/api-test/src/main/test/java/io/metersphere/api/controller/ApiDefinitionControllerTests.java @@ -1,22 +1,30 @@ package io.metersphere.api.controller; +import io.metersphere.api.domain.ApiDefinition; import io.metersphere.api.dto.ApiDefinitionDTO; +import io.metersphere.api.dto.ApiDefinitionListRequest; +import io.metersphere.sdk.controller.handler.ResultHolder; +import io.metersphere.sdk.util.JSON; import io.metersphere.sdk.util.LogUtils; +import io.metersphere.sdk.util.Pager; import io.metersphere.utils.JsonUtils; import jakarta.annotation.Resource; import org.junit.Before; -import org.junit.jupiter.api.MethodOrderer; -import org.junit.jupiter.api.Order; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.TestMethodOrder; +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.mock.web.MockHttpServletResponse; import org.springframework.mock.web.MockMultipartFile; +import org.springframework.test.context.jdbc.Sql; +import org.springframework.test.context.jdbc.SqlConfig; import org.springframework.test.web.servlet.MockMvc; +import org.springframework.test.web.servlet.MvcResult; import org.springframework.test.web.servlet.request.MockMvcRequestBuilders; import org.springframework.test.web.servlet.result.MockMvcResultMatchers; +import java.util.List; + import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath; @@ -68,4 +76,75 @@ public class ApiDefinitionControllerTests { .andExpect(MockMvcResultMatchers.content().contentType(MediaType.APPLICATION_JSON)) .andExpect(jsonPath("$.data.id").value("test-api-id")); } + + //正常接口获取 + @Test + @Sql(scripts = {"/sql/init_api_definition.sql"}, + config = @SqlConfig(encoding = "utf-8", transactionMode = SqlConfig.TransactionMode.ISOLATED), + executionPhase = Sql.ExecutionPhase.BEFORE_TEST_METHOD) + @Order(2) + public void listSuccess() throws Exception { + int pageSize = 10; + int current = 1; + ApiDefinitionListRequest request = new ApiDefinitionListRequest(); + request.setCurrent(current); + request.setPageSize(pageSize); + request.setProjectId("test-project-id"); + + MvcResult mvcResult = mockMvc.perform(MockMvcRequestBuilders.multipart(prefix + "/page") + .contentType(MediaType.APPLICATION_JSON_VALUE) + .content(JsonUtils.toJSONString(request))) + .andExpect(MockMvcResultMatchers.status().isOk()) + .andReturn(); + + MockHttpServletResponse mockResponse = mvcResult.getResponse(); + + String returnData = mockResponse.getContentAsString(); + ResultHolder resultHolder = JsonUtils.parseObject(returnData, ResultHolder.class); + + //返回请求正常 + Assertions.assertNotNull(resultHolder); + + Pager returnPager = JSON.parseObject(JSON.toJSONString(resultHolder.getData()), Pager.class); + + //返回值不为空 + Assertions.assertNotNull(returnPager); + //返回值的页码和当前页码相同 + Assertions.assertEquals(returnPager.getCurrent(), current); + //返回的数据量不超过规定要返回的数据量相同 + Assertions.assertTrue(((List) returnPager.getList()).size() <= pageSize); + } + + //没有传入必填值 + @Test + @Order(3) + public void listError() throws Exception { + // projectId为空 + ApiDefinitionListRequest request = new ApiDefinitionListRequest(); + request.setCurrent(1); + request.setPageSize(20); + mockMvc.perform(MockMvcRequestBuilders.multipart(prefix + "/page") + .contentType(MediaType.APPLICATION_JSON_VALUE) + .content(JsonUtils.toJSONString(request))) + .andExpect(MockMvcResultMatchers.status().isBadRequest()); + + //pageSize为空 + request = new ApiDefinitionListRequest(); + request.setCurrent(1); + request.setProjectId("test-project-id"); + mockMvc.perform(MockMvcRequestBuilders.multipart(prefix + "/page") + .contentType(MediaType.APPLICATION_JSON_VALUE) + .content(JsonUtils.toJSONString(request))) + .andExpect(MockMvcResultMatchers.status().isBadRequest()); + + //current为空 + request = new ApiDefinitionListRequest(); + request.setPageSize(20); + request.setProjectId("test-project-id"); + mockMvc.perform(MockMvcRequestBuilders.multipart(prefix + "/page") + .contentType(MediaType.APPLICATION_JSON_VALUE) + .content(JsonUtils.toJSONString(request))) + .andExpect(MockMvcResultMatchers.status().isBadRequest()); + } + } diff --git a/backend/services/api-test/src/main/test/resources/sql/init_api_definition.sql b/backend/services/api-test/src/main/test/resources/sql/init_api_definition.sql new file mode 100644 index 0000000000..12f0e014b2 --- /dev/null +++ b/backend/services/api-test/src/main/test/resources/sql/init_api_definition.sql @@ -0,0 +1,8 @@ +# 插入测试数据 +INSERT INTO `api_definition` VALUES ('101', 1, 'sty', 1, 'sty', NULL, NULL, b'0', 'sty-test-11', 'TCP', 'TCP', '', NULL, 'Prepared', 'NONE', NULL, NULL, 1, b'0', NULL, 'test-project-id', NULL, b'0', NULL, NULL, NULL); +INSERT INTO `api_definition` VALUES ('102', 1, 'sty', 1, 'sty', NULL, NULL, b'0', 'sty-test-12', 'TCP', 'TCP', '', NULL, 'Prepared', 'NONE', NULL, NULL, 1, b'0', NULL, 'test-project-id', NULL, b'0', NULL, NULL, NULL); +INSERT INTO `api_definition` VALUES ('103', 1, 'sty', 1, 'sty', NULL, NULL, b'0', 'sty-test-13', 'TCP', 'TCP', '', NULL, 'Prepared', 'NONE', NULL, NULL, 1, b'0', NULL, 'test-project-id', NULL, b'0', NULL, NULL, NULL); +INSERT INTO `api_definition` VALUES ('104', 1, 'sty', 1, 'sty', NULL, NULL, b'0', 'sty-test-14', 'TCP', 'TCP', '', NULL, 'Prepared', 'NONE', NULL, NULL, 1, b'0', NULL, 'test-project-id', NULL, b'0', NULL, NULL, NULL); +INSERT INTO `api_definition` VALUES ('105', 1, 'sty', 1, 'sty', NULL, NULL, b'0', 'sty-test-15', 'TCP', 'TCP', '', NULL, 'Prepared', 'NONE', NULL, NULL, 1, b'0', NULL, 'test-project-id', NULL, b'0', NULL, NULL, NULL); +INSERT INTO `api_definition` VALUES ('106', 1, 'sty', 1, 'sty', NULL, NULL, b'0', 'sty-test-16', 'TCP', 'TCP', '', NULL, 'Prepared', 'NONE', NULL, NULL, 1, b'0', NULL, 'test-project-id', NULL, b'0', NULL, NULL, NULL); +INSERT INTO `api_definition` VALUES ('107', 1, 'sty', 1, 'sty', NULL, NULL, b'0', 'sty-test-17', 'TCP', 'TCP', '', NULL, 'Prepared', 'NONE', NULL, NULL, 1, b'0', NULL, 'test-project-id', NULL, b'0', NULL, NULL, NULL);