feat(接口测试): 接口管理模块接口文档分享
This commit is contained in:
parent
ca1217b724
commit
6be702af86
|
@ -156,3 +156,17 @@ CREATE INDEX idx_type ON operation_history (`type`);
|
|||
|
||||
-- set innodb lock wait timeout to default
|
||||
SET SESSION innodb_lock_wait_timeout = DEFAULT;
|
||||
|
||||
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 '语言' ,
|
||||
PRIMARY KEY (id)
|
||||
) ENGINE = InnoDB
|
||||
DEFAULT CHARSET = utf8mb4
|
||||
COLLATE = utf8mb4_general_ci COMMENT = '分享';
|
||||
|
|
|
@ -44,6 +44,7 @@ public class FilterChainUtils {
|
|||
filterChainDefinitionMap.put("/anonymous/**", "anon");
|
||||
|
||||
//分享相关接口
|
||||
filterChainDefinitionMap.put("/api/share/doc/view/**", "anon");
|
||||
|
||||
filterChainDefinitionMap.put("/system/theme", "anon");
|
||||
filterChainDefinitionMap.put("/system/parameter/save/base-url/**", "anon");
|
||||
|
|
|
@ -0,0 +1,10 @@
|
|||
package io.metersphere.api.constants;
|
||||
|
||||
/**
|
||||
* @author: LAN
|
||||
* @date: 2023/12/27 15:32
|
||||
* @version: 1.0
|
||||
*/
|
||||
public enum ShareInfoType {
|
||||
Single, Batch
|
||||
}
|
|
@ -187,7 +187,7 @@ public class ApiDefinitionController {
|
|||
@RequiresPermissions(PermissionConstants.PROJECT_API_DEFINITION_READ)
|
||||
@CheckOwner(resourceId = "#request.getProjectId()", resourceType = "project")
|
||||
public ApiDefinitionDocDTO getDocInfo(@Validated @RequestBody ApiDefinitionDocRequest request) {
|
||||
return apiDefinitionService.getDocInfo(request, SessionUtils.getUserId());
|
||||
return apiDefinitionService.getDocInfo(request);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -0,0 +1,46 @@
|
|||
package io.metersphere.api.controller.definition;
|
||||
|
||||
import io.metersphere.api.dto.definition.ApiDefinitionDocDTO;
|
||||
import io.metersphere.api.dto.definition.ApiDefinitionDocRequest;
|
||||
import io.metersphere.api.dto.share.ShareInfoDTO;
|
||||
import io.metersphere.api.service.ApiShareService;
|
||||
import io.metersphere.sdk.constants.PermissionConstants;
|
||||
import io.metersphere.system.security.CheckOwner;
|
||||
import io.metersphere.system.utils.SessionUtils;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import jakarta.annotation.Resource;
|
||||
import org.apache.shiro.authz.annotation.RequiresPermissions;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* @author: LAN
|
||||
* @date: 2023/12/27 14:30
|
||||
* @version: 1.0
|
||||
*/
|
||||
@RestController
|
||||
@RequestMapping(value = "/api/share")
|
||||
@Tag(name = "接口测试-接口管理-接口分享")
|
||||
public class ApiShareController {
|
||||
|
||||
@Resource
|
||||
private ApiShareService apiShareService;
|
||||
|
||||
@PostMapping("/doc/gen")
|
||||
@Operation(summary = "接口测试-接口管理-接口文档分享")
|
||||
@RequiresPermissions(PermissionConstants.PROJECT_API_DEFINITION_READ)
|
||||
@CheckOwner(resourceId = "#request.getProjectId()", resourceType = "project")
|
||||
public ShareInfoDTO genApiDocShareInfo(@RequestBody ApiDefinitionDocRequest request) {
|
||||
return apiShareService.genApiDocShareInfo(request, Objects.requireNonNull(SessionUtils.getUser()));
|
||||
}
|
||||
|
||||
@GetMapping("/doc/view/{shareId}")
|
||||
@Operation(summary = "接口测试-接口管理-接口文档分享查看")
|
||||
@RequiresPermissions(PermissionConstants.PROJECT_API_DEFINITION_READ)
|
||||
@CheckOwner(resourceId = "#shareId", resourceType = "share_info")
|
||||
public ApiDefinitionDocDTO shareDocView(@PathVariable String shareId) {
|
||||
return apiShareService.shareDocView(shareId);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,14 @@
|
|||
package io.metersphere.api.dto.share;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* @author: LAN
|
||||
* @date: 2023/12/27 14:43
|
||||
* @version: 1.0
|
||||
*/
|
||||
@Data
|
||||
public class ShareInfoDTO {
|
||||
private String id;
|
||||
private String shareUrl;
|
||||
}
|
|
@ -0,0 +1,29 @@
|
|||
package io.metersphere.api.dto.share.request;
|
||||
|
||||
import io.metersphere.api.dto.definition.ApiDefinitionDocRequest;
|
||||
import io.metersphere.sdk.domain.ShareInfo;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
import jakarta.validation.constraints.Size;
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* @author: LAN
|
||||
* @date: 2023/12/27 14:46
|
||||
* @version: 1.0
|
||||
*/
|
||||
@Data
|
||||
public class ApiDocShareRequest extends ShareInfo {
|
||||
|
||||
@Schema(description = "接口pk")
|
||||
@Size(min = 1, max = 50, message = "{api_definition.id.length_range}")
|
||||
private String apiId;
|
||||
|
||||
@Schema(description = "项目ID", requiredMode = Schema.RequiredMode.REQUIRED)
|
||||
@NotBlank(message = "{api_definition.project_id.not_blank}")
|
||||
@Size(min = 1, max = 50, message = "{api_definition.project_id.length_range}")
|
||||
private String projectId;
|
||||
|
||||
@Schema(description = "接口文档分享选择")
|
||||
private ApiDefinitionDocRequest selectRequest;
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
package io.metersphere.api.mapper;
|
||||
|
||||
import io.metersphere.sdk.domain.ShareInfo;
|
||||
import org.apache.ibatis.annotations.Param;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public interface ExtShareInfoMapper {
|
||||
|
||||
List<ShareInfo> selectByShareTypeAndShareApiIdWithBLOBs(@Param("shareType") String shareType, @Param("customData") byte[] customData, @Param("lang") String lang);
|
||||
}
|
|
@ -0,0 +1,18 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
|
||||
<mapper namespace="io.metersphere.api.mapper.ExtShareInfoMapper">
|
||||
|
||||
<select id="selectByShareTypeAndShareApiIdWithBLOBs" resultType="io.metersphere.sdk.domain.ShareInfo">
|
||||
SELECT id,share_type,custom_data FROM share_info
|
||||
<where>
|
||||
share_type = #{shareType}
|
||||
<if test="customData != null and customData != ''">
|
||||
AND custom_data = #{customData}
|
||||
</if>
|
||||
<if test="lang != null and lang != ''">
|
||||
AND lang = #{lang}
|
||||
</if>
|
||||
</where>
|
||||
</select>
|
||||
|
||||
</mapper>
|
|
@ -0,0 +1,117 @@
|
|||
package io.metersphere.api.service;
|
||||
|
||||
import io.metersphere.api.constants.ApiDefinitionDocType;
|
||||
import io.metersphere.api.constants.ShareInfoType;
|
||||
import io.metersphere.api.dto.definition.ApiDefinitionDocDTO;
|
||||
import io.metersphere.api.dto.definition.ApiDefinitionDocRequest;
|
||||
import io.metersphere.api.dto.share.ShareInfoDTO;
|
||||
import io.metersphere.api.mapper.ExtShareInfoMapper;
|
||||
import io.metersphere.api.service.definition.ApiDefinitionService;
|
||||
import io.metersphere.api.utils.ApiDataUtils;
|
||||
import io.metersphere.sdk.domain.ShareInfo;
|
||||
import io.metersphere.sdk.mapper.ShareInfoMapper;
|
||||
import io.metersphere.sdk.util.BeanUtils;
|
||||
import io.metersphere.sdk.util.JSON;
|
||||
import io.metersphere.system.dto.sdk.SessionUser;
|
||||
import jakarta.annotation.Resource;
|
||||
import org.springframework.context.i18n.LocaleContextHolder;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* @author: LAN
|
||||
* @date: 2023/12/27 14:47
|
||||
* @version: 1.0
|
||||
*/
|
||||
@Service
|
||||
public class ApiShareService {
|
||||
|
||||
@Resource
|
||||
ShareInfoMapper shareInfoMapper;
|
||||
|
||||
@Resource
|
||||
ExtShareInfoMapper extShareInfoMapper;
|
||||
|
||||
@Resource
|
||||
ApiDefinitionService apiDefinitionService;
|
||||
|
||||
/**
|
||||
* 生成 api 接口文档分享信息
|
||||
* 根据要分享的类型来进行完全匹配搜索。
|
||||
* 搜索的到就返回那条数据,搜索不到就新增一条信息
|
||||
*/
|
||||
public ShareInfoDTO genApiDocShareInfo(ApiDefinitionDocRequest request, SessionUser user) {
|
||||
ShareInfo shareInfoRequest = new ShareInfo();
|
||||
String customData = genCustomData(request, shareInfoRequest);
|
||||
String lang = user.getLanguage() == null ? LocaleContextHolder.getLocale().toString() : user.getLanguage();
|
||||
|
||||
Optional.ofNullable(customData)
|
||||
.ifPresent(data -> {
|
||||
BeanUtils.copyBean(shareInfoRequest, request);
|
||||
shareInfoRequest.setCreateUser(user.getId());
|
||||
shareInfoRequest.setLang(lang);
|
||||
shareInfoRequest.setCustomData(data.getBytes());
|
||||
});
|
||||
ShareInfo shareInfo = Optional.ofNullable(customData)
|
||||
.map(data -> genShareInfo(shareInfoRequest))
|
||||
.orElse(new ShareInfo());
|
||||
|
||||
return 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());
|
||||
} else if (ApiDefinitionDocType.API.name().equals(request.getType())) {
|
||||
apiDefinitionService.checkApiDefinition(request.getApiId());
|
||||
customData = JSON.toJSONString(request);
|
||||
shareInfoRequest.setShareType(ShareInfoType.Single.name());
|
||||
}
|
||||
|
||||
return customData;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* 生成分享连接
|
||||
* 如果该数据有连接则返回已有的连接,不做有效期判断, 反之创建链接
|
||||
*
|
||||
* @param request 分享请求内容
|
||||
* @return 分享数据
|
||||
*/
|
||||
public ShareInfo genShareInfo(ShareInfo request) {
|
||||
List<ShareInfo> shareInfos = extShareInfoMapper.selectByShareTypeAndShareApiIdWithBLOBs(request.getShareType(), request.getCustomData(), request.getLang());
|
||||
return shareInfos.isEmpty() ? createShareInfo(request) : shareInfos.get(0);
|
||||
}
|
||||
|
||||
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);
|
||||
ApiDefinitionDocRequest apiDefinitionDocRequest = ApiDataUtils.parseObject(new String(shareInfo.getCustomData()), ApiDefinitionDocRequest.class);
|
||||
return apiDefinitionService.getDocInfo(apiDefinitionDocRequest);
|
||||
}
|
||||
}
|
|
@ -2,10 +2,7 @@ package io.metersphere.api.service.definition;
|
|||
|
||||
import io.metersphere.api.constants.ApiResourceType;
|
||||
import io.metersphere.api.controller.result.ApiResultCode;
|
||||
import io.metersphere.api.domain.ApiDefinition;
|
||||
import io.metersphere.api.domain.ApiDefinitionMock;
|
||||
import io.metersphere.api.domain.ApiDefinitionMockConfig;
|
||||
import io.metersphere.api.domain.ApiDefinitionMockExample;
|
||||
import io.metersphere.api.domain.*;
|
||||
import io.metersphere.api.dto.debug.ApiFileResourceUpdateRequest;
|
||||
import io.metersphere.api.dto.definition.ApiDefinitionMockDTO;
|
||||
import io.metersphere.api.dto.definition.HttpResponse;
|
||||
|
@ -198,7 +195,7 @@ public class ApiDefinitionMockService {
|
|||
public void delete(ApiDefinitionMockRequest request, String userId) {
|
||||
checkApiDefinitionMock(request.getId());
|
||||
String apiDefinitionMockDir = DefaultRepositoryDir.getApiDefinitionDir(request.getProjectId(), request.getId());
|
||||
apiFileResourceService.deleteByResourceId(apiDefinitionMockDir, request.getId(), request.getProjectId(), userId, OperationLogModule.API_DEFINITION);
|
||||
apiFileResourceService.deleteByResourceId(apiDefinitionMockDir, request.getId(), request.getProjectId(), userId, OperationLogModule.API_DEFINITION_MOCK);
|
||||
apiDefinitionMockConfigMapper.deleteByPrimaryKey(request.getId());
|
||||
apiDefinitionMockMapper.deleteByPrimaryKey(request.getId());
|
||||
}
|
||||
|
@ -251,4 +248,26 @@ public class ApiDefinitionMockService {
|
|||
public String uploadTempFile(MultipartFile file) {
|
||||
return apiFileResourceService.uploadTempFile(file);
|
||||
}
|
||||
|
||||
public void deleteByApiIds(List<String> apiIds, String userId) {
|
||||
ApiDefinitionMockExample apiDefinitionMockExample = new ApiDefinitionMockExample();
|
||||
apiDefinitionMockExample.createCriteria().andApiDefinitionIdIn(apiIds);
|
||||
|
||||
List<ApiDefinitionMock> apiDefinitionMocks = apiDefinitionMockMapper.selectByExample(apiDefinitionMockExample);
|
||||
|
||||
if(!apiDefinitionMocks.isEmpty()){
|
||||
apiDefinitionMocks.forEach(item -> {
|
||||
String apiDefinitionMockDir = DefaultRepositoryDir.getApiDefinitionDir(item.getProjectId(), item.getId());
|
||||
apiFileResourceService.deleteByResourceId(apiDefinitionMockDir, item.getId(), item.getProjectId(), userId, OperationLogModule.API_DEFINITION_MOCK);
|
||||
});
|
||||
|
||||
List<String> mockIds = apiDefinitionMocks.stream().map(ApiDefinitionMock::getId).toList();
|
||||
|
||||
ApiDefinitionMockConfigExample apiDefinitionMockConfigExample = new ApiDefinitionMockConfigExample();
|
||||
apiDefinitionMockConfigExample.createCriteria().andIdIn(mockIds);
|
||||
apiDefinitionMockConfigMapper.deleteByExample(apiDefinitionMockConfigExample);
|
||||
|
||||
apiDefinitionMockMapper.deleteByExample(apiDefinitionMockExample);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -94,6 +94,9 @@ public class ApiDefinitionService {
|
|||
@Resource
|
||||
private ApiDefinitionLogService apiDefinitionLogService;
|
||||
|
||||
@Resource
|
||||
private ApiDefinitionMockService apiDefinitionMockService;
|
||||
|
||||
public List<ApiDefinitionDTO> getApiDefinitionPage(ApiDefinitionPageRequest request, String userId){
|
||||
CustomFieldUtils.setBaseQueryRequestCustomMultipleFields(request, userId);
|
||||
List<ApiDefinitionDTO> list = extApiDefinitionMapper.list(request);
|
||||
|
@ -446,7 +449,7 @@ public class ApiDefinitionService {
|
|||
*
|
||||
* @param apiId 接口id
|
||||
*/
|
||||
private ApiDefinition checkApiDefinition(String apiId) {
|
||||
public ApiDefinition checkApiDefinition(String apiId) {
|
||||
ApiDefinition apiDefinition = apiDefinitionMapper.selectByPrimaryKey(apiId);
|
||||
if (apiDefinition == null) {
|
||||
throw new MSException(ApiResultCode.API_DEFINITION_NOT_EXIST);
|
||||
|
@ -777,8 +780,9 @@ public class ApiDefinitionService {
|
|||
List<String> caseIds = caseLists.stream().map(ApiTestCase::getId).distinct().toList();
|
||||
// case 批量删除回收站
|
||||
apiTestCaseService.deleteResourceByIds(caseIds, projectId, userId);
|
||||
|
||||
}
|
||||
// 删除 mock
|
||||
apiDefinitionMockService.deleteByApiIds(apiIds, userId);
|
||||
}
|
||||
|
||||
// 获取批量操作选中的ID
|
||||
|
@ -821,7 +825,7 @@ public class ApiDefinitionService {
|
|||
apiDefinitionDTO.setCustomFields(customFieldMap.get(id));
|
||||
}
|
||||
|
||||
public ApiDefinitionDocDTO getDocInfo(ApiDefinitionDocRequest request, String userId) {
|
||||
public ApiDefinitionDocDTO getDocInfo(ApiDefinitionDocRequest request) {
|
||||
ApiDefinitionDocDTO apiDefinitionDocDTO = new ApiDefinitionDocDTO();
|
||||
apiDefinitionDocDTO.setType(request.getType());
|
||||
// 下载所有/一个模块接口文档时,不做分页数据量大的时候会不会有性能问题,单独做接口
|
||||
|
@ -830,20 +834,21 @@ public class ApiDefinitionService {
|
|||
if (!list.isEmpty()) {
|
||||
ApiDefinitionDTO first = list.get(0);
|
||||
apiDefinitionLogService.handleBlob(first.getId(), first);
|
||||
if(ApiDefinitionDocType.ALL.name().equals(request.getType())){
|
||||
apiDefinitionDocDTO.setDocTitle(Translator.get(ALL_API));
|
||||
String docTitle;
|
||||
if (ApiDefinitionDocType.ALL.name().equals(request.getType())) {
|
||||
docTitle = Translator.get(ALL_API);
|
||||
} else {
|
||||
ApiDefinitionModule apiDefinitionModule = apiDefinitionModuleMapper.selectByPrimaryKey(first.getModuleId());
|
||||
if (apiDefinitionModule != null) {
|
||||
apiDefinitionDocDTO.setDocTitle(apiDefinitionModule.getName());
|
||||
} else {
|
||||
apiDefinitionDocDTO.setDocTitle(Translator.get(UNPLANNED_API));
|
||||
}
|
||||
docTitle = (apiDefinitionModule != null) ? apiDefinitionModule.getName() : Translator.get(UNPLANNED_API);
|
||||
}
|
||||
apiDefinitionDocDTO.setDocTitle(docTitle);
|
||||
apiDefinitionDocDTO.setDocInfo(first);
|
||||
}
|
||||
} else if (ApiDefinitionDocType.API.name().equals(request.getType())) {
|
||||
ApiDefinitionDTO apiDefinitionDTO = get(request.getApiId(), userId);
|
||||
ApiDefinition apiDefinition = checkApiDefinition(request.getApiId());
|
||||
ApiDefinitionDTO apiDefinitionDTO = new ApiDefinitionDTO();
|
||||
BeanUtils.copyBean(apiDefinitionDTO, apiDefinition);
|
||||
apiDefinitionLogService.handleBlob(apiDefinition.getId(), apiDefinitionDTO);
|
||||
apiDefinitionDocDTO.setDocTitle(apiDefinitionDTO.getName());
|
||||
apiDefinitionDocDTO.setDocInfo(apiDefinitionDTO);
|
||||
}
|
||||
|
|
|
@ -0,0 +1,159 @@
|
|||
package io.metersphere.api.controller;
|
||||
|
||||
import io.metersphere.api.constants.ApiDefinitionDocType;
|
||||
import io.metersphere.api.constants.ShareInfoType;
|
||||
import io.metersphere.api.controller.result.ApiResultCode;
|
||||
import io.metersphere.api.domain.ApiDefinition;
|
||||
import io.metersphere.api.domain.ApiDefinitionBlob;
|
||||
import io.metersphere.api.dto.definition.ApiDefinitionDTO;
|
||||
import io.metersphere.api.dto.definition.ApiDefinitionDocDTO;
|
||||
import io.metersphere.api.dto.definition.ApiDefinitionDocRequest;
|
||||
import io.metersphere.api.dto.definition.HttpResponse;
|
||||
import io.metersphere.api.dto.request.http.MsHTTPElement;
|
||||
import io.metersphere.api.dto.share.ShareInfoDTO;
|
||||
import io.metersphere.api.mapper.ApiDefinitionBlobMapper;
|
||||
import io.metersphere.api.mapper.ApiDefinitionMapper;
|
||||
import io.metersphere.api.mapper.ExtApiDefinitionMapper;
|
||||
import io.metersphere.api.mapper.ExtShareInfoMapper;
|
||||
import io.metersphere.api.utils.ApiDataUtils;
|
||||
import io.metersphere.plugin.api.spi.AbstractMsTestElement;
|
||||
import io.metersphere.sdk.constants.PermissionConstants;
|
||||
import io.metersphere.sdk.domain.ShareInfo;
|
||||
import io.metersphere.sdk.exception.MSException;
|
||||
import io.metersphere.sdk.mapper.ShareInfoMapper;
|
||||
import io.metersphere.sdk.util.JSON;
|
||||
import io.metersphere.sdk.util.Translator;
|
||||
import io.metersphere.system.base.BaseTest;
|
||||
import jakarta.annotation.Resource;
|
||||
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.test.context.jdbc.Sql;
|
||||
import org.springframework.test.context.jdbc.SqlConfig;
|
||||
import org.springframework.test.web.servlet.MvcResult;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@SpringBootTest(webEnvironment= SpringBootTest.WebEnvironment.RANDOM_PORT)
|
||||
@TestMethodOrder(MethodOrderer.OrderAnnotation.class)
|
||||
@AutoConfigureMockMvc
|
||||
public class ApiShareControllerTests extends BaseTest {
|
||||
|
||||
private static final String BASE_PATH = "/api/share/";
|
||||
|
||||
private static final String SHARE_DOC = BASE_PATH + "/doc/gen";
|
||||
private static final String SHARE_VIEW = BASE_PATH + "/doc/view/";
|
||||
|
||||
private static final String ALL_API = "api_definition_module.api.all";
|
||||
|
||||
@Resource
|
||||
private ApiDefinitionMapper apiDefinitionMapper;
|
||||
|
||||
@Resource
|
||||
private ApiDefinitionBlobMapper apiDefinitionBlobMapper;
|
||||
|
||||
@Resource
|
||||
private ExtApiDefinitionMapper extApiDefinitionMapper;
|
||||
|
||||
@Resource
|
||||
private ExtShareInfoMapper extShareInfoMapper;
|
||||
|
||||
@Resource
|
||||
private ShareInfoMapper shareInfoMapper;
|
||||
|
||||
|
||||
@Test
|
||||
@Order(1)
|
||||
@Sql(scripts = {"/dml/init_api_definition.sql"}, config = @SqlConfig(encoding = "utf-8", transactionMode = SqlConfig.TransactionMode.ISOLATED))
|
||||
public void testShareDoc() throws Exception {
|
||||
ApiDefinitionDocRequest request = new ApiDefinitionDocRequest();
|
||||
ApiDefinition apiDefinition = apiDefinitionMapper.selectByPrimaryKey("1001");
|
||||
request.setApiId(apiDefinition.getId());
|
||||
request.setProjectId(DEFAULT_PROJECT_ID);
|
||||
request.setType(ApiDefinitionDocType.API.name());
|
||||
// @@请求成功
|
||||
this.requestPostWithOkAndReturn(SHARE_DOC, request);
|
||||
MvcResult mvcResult = this.requestPostWithOkAndReturn(SHARE_DOC, request);
|
||||
ApiDataUtils.setResolver(MsHTTPElement.class);
|
||||
ShareInfoDTO shareInfoDTO = ApiDataUtils.parseObject(JSON.toJSONString(parseResponse(mvcResult).get("data")), ShareInfoDTO.class);
|
||||
// 校验数据是否正确
|
||||
List<ShareInfo> 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());
|
||||
Assertions.assertTrue(shareInfoDTO.getShareUrl().contains("?shareId="));
|
||||
|
||||
request.setApiId("111");
|
||||
assertErrorCode(this.requestPost(SHARE_DOC, request), ApiResultCode.API_DEFINITION_NOT_EXIST);
|
||||
|
||||
// @@分享模块文档
|
||||
request.setApiId(null);
|
||||
request.setProjectId(DEFAULT_PROJECT_ID);
|
||||
request.setType(ApiDefinitionDocType.MODULE.name());
|
||||
request.setModuleIds(List.of("10001"));
|
||||
MvcResult mvcResultModule = this.requestPostWithOkAndReturn(SHARE_DOC, request);
|
||||
ApiDataUtils.setResolver(MsHTTPElement.class);
|
||||
ShareInfoDTO shareInfoDTOModule = ApiDataUtils.parseObject(JSON.toJSONString(parseResponse(mvcResultModule).get("data")), ShareInfoDTO.class);
|
||||
// 校验数据是否正确
|
||||
List<ShareInfo> 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());
|
||||
Assertions.assertTrue(shareInfoDTOModule.getShareUrl().contains("?shareId="));
|
||||
|
||||
// @@分享全部文档
|
||||
request.setApiId(null);
|
||||
request.setModuleIds(null);
|
||||
request.setProjectId(DEFAULT_PROJECT_ID);
|
||||
request.setType(ApiDefinitionDocType.ALL.name());
|
||||
MvcResult mvcResultAll = this.requestPostWithOkAndReturn(SHARE_DOC, request);
|
||||
ApiDataUtils.setResolver(MsHTTPElement.class);
|
||||
ShareInfoDTO allShareInfoDTO = ApiDataUtils.parseObject(JSON.toJSONString(parseResponse(mvcResultAll).get("data")), ShareInfoDTO.class);
|
||||
|
||||
// 校验数据是否正确
|
||||
List<ShareInfo> 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());
|
||||
Assertions.assertTrue(allShareInfoDTO.getShareUrl().contains("?shareId="));
|
||||
assertTestShareView(allShareInfoDTO.getId());
|
||||
|
||||
// @@校验权限
|
||||
requestPostPermissionTest(PermissionConstants.PROJECT_API_DEFINITION_READ, SHARE_DOC, request);
|
||||
}
|
||||
|
||||
|
||||
public void assertTestShareView(String shareId) throws Exception {
|
||||
// @@请求成功
|
||||
ShareInfo shareInfo = shareInfoMapper.selectByPrimaryKey(shareId);
|
||||
ApiDefinitionDocRequest apiDefinitionDocRequest = ApiDataUtils.parseObject(new String(shareInfo.getCustomData()), ApiDefinitionDocRequest.class);
|
||||
MvcResult mvcResultAll = this.requestGetWithOkAndReturn(SHARE_VIEW + shareId);
|
||||
ApiDataUtils.setResolver(MsHTTPElement.class);
|
||||
ApiDefinitionDocDTO allApiDefinitionDocDTO = ApiDataUtils.parseObject(JSON.toJSONString(parseResponse(mvcResultAll).get("data")), ApiDefinitionDocDTO.class);
|
||||
// 校验数据是否正确
|
||||
ApiDefinitionDocDTO copyAllApiDefinitionDocDTO = new ApiDefinitionDocDTO();
|
||||
|
||||
List<ApiDefinitionDTO> allList = extApiDefinitionMapper.listDoc(apiDefinitionDocRequest);
|
||||
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){
|
||||
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())){
|
||||
copyAllApiDefinitionDocDTO.setDocTitle(Translator.get(ALL_API));
|
||||
}
|
||||
copyAllApiDefinitionDocDTO.setType(ApiDefinitionDocType.ALL.name());
|
||||
copyAllApiDefinitionDocDTO.setDocInfo(info);
|
||||
}
|
||||
|
||||
Assertions.assertEquals(allApiDefinitionDocDTO.getType(), copyAllApiDefinitionDocDTO.getType());
|
||||
Assertions.assertEquals(allApiDefinitionDocDTO.getDocTitle(), copyAllApiDefinitionDocDTO.getDocTitle());
|
||||
Assertions.assertEquals(allApiDefinitionDocDTO.getDocInfo().getId(), copyAllApiDefinitionDocDTO.getDocInfo().getId());
|
||||
// @@校验权限
|
||||
requestGetPermissionTest(PermissionConstants.PROJECT_API_DEFINITION_READ, SHARE_VIEW + shareId);
|
||||
}
|
||||
|
||||
}
|
|
@ -57,3 +57,21 @@ DELETE FROM `template` WHERE `id` in ('api-template-id', 'default-api-template-i
|
|||
INSERT INTO template (id, name, remark, internal, update_time, create_time, create_user, scope_type, scope_id, enable_third_part, scene) VALUES
|
||||
('api-template-id', 'api-template', '', 0, UNIX_TIMESTAMP() * 1000, UNIX_TIMESTAMP() * 1000, 'admin', 'PROJECT', '100001100001', 0, 'API'),
|
||||
('default-api-template-id', 'api-default-template', '', 0, UNIX_TIMESTAMP() * 1000, UNIX_TIMESTAMP() * 1000, 'admin', 'PROJECT', '100001100001', 0, 'API');
|
||||
|
||||
|
||||
DELETE FROM `api_definition_mock` WHERE `id` in ('mock_1', 'mock_2', 'mock_3', 'mock_4','mock_5');
|
||||
INSERT INTO `api_definition_mock` VALUES
|
||||
('mock_1', 1641120000000, 1641120000000, 'user1', 'Mock 1', '[\"tag1\",\"tag2\"]', 1, 'EXPECT001', '100001100001', '1001'),
|
||||
('mock_2', 1641121000000, 1641121000000, 'user2', 'Mock 2', '[\"tag2\",\"tag3\"]', 1, 'EXPECT002', '100001100001', '1002'),
|
||||
('mock_3', 1641122000000, 1641122000000, 'user3', 'Mock 3', '[\"tag3\",\"tag4\"]', 1, 'EXPECT003', '100001100001', '1003'),
|
||||
('mock_4', 1641123000000, 1641123000000, 'user1', 'Mock 4', '[\"tag4\",\"tag5\"]', 1, 'EXPECT004', '100001100001', '1005'),
|
||||
('mock_5', 1641124000000, 1641124000000, 'user2', 'Mock 5', '[\"tag5\",\"tag1\"]', 1, 'EXPECT005', '100001100001', '1005');
|
||||
|
||||
DELETE FROM `api_definition_mock_config` WHERE `id` in ('mock_1', 'mock_2', 'mock_3', 'mock_4','mock_5');
|
||||
INSERT INTO `api_definition_mock_config` VALUES
|
||||
('mock_1', '{"type": "exact", "value": "request_value"}', '{"status": 200, "body": {"message": "Mock Response 1"}}'),
|
||||
('mock_2', '{"type": "regex", "value": "\\d{3}"}', '{"status": 404, "body": {"error": "Not Found"}}'),
|
||||
('mock_3', '{"type": "contains", "value": "partial_value"}', '{"status": 500, "body": {"error": "Internal Server Error"}}'),
|
||||
('mock_4', '{"type": "exact", "value": "another_exact_value"}', '{"status": 200, "body": {"data": "Another Mock Response"}}'),
|
||||
('mock_5', '{"type": "jsonpath", "value": "$.items[0].name"}', '{"status": 200, "body": {"items": [{"name": "Item 1"}]}}');
|
||||
|
||||
|
|
|
@ -18,6 +18,9 @@ public class ApiTestInterceptor {
|
|||
configList.add(new MybatisInterceptorConfig(ApiDefinitionBlob.class, "request", CompressUtils.class, "zip", "unzip"));
|
||||
configList.add(new MybatisInterceptorConfig(ApiDefinitionBlob.class, "response", CompressUtils.class, "zip", "unzip"));
|
||||
configList.add(new MybatisInterceptorConfig(ApiDefinitionBlob.class, "remark", CompressUtils.class, "zip", "unzip"));
|
||||
// ApiDefinitionMockConfig
|
||||
configList.add(new MybatisInterceptorConfig(ApiDefinitionMockConfig.class, "matching", CompressUtils.class, "zip", "unzip"));
|
||||
configList.add(new MybatisInterceptorConfig(ApiDefinitionMockConfig.class, "response", CompressUtils.class, "zip", "unzip"));
|
||||
// ApiTestCaseBlob
|
||||
configList.add(new MybatisInterceptorConfig(ApiTestCaseBlob.class, "request", CompressUtils.class, "zip", "unzip"));
|
||||
// ApiReportBlob
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
package io.metersphere.system.config.interceptor;
|
||||
|
||||
import io.metersphere.sdk.domain.OperationLogBlob;
|
||||
import io.metersphere.sdk.domain.ShareInfo;
|
||||
import io.metersphere.sdk.util.CompressUtils;
|
||||
import io.metersphere.system.utils.MybatisInterceptorConfig;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
|
@ -17,6 +18,8 @@ public class SdkInterceptor {
|
|||
|
||||
configList.add(new MybatisInterceptorConfig(OperationLogBlob.class, "originalValue", CompressUtils.class, "zip", "unzip"));
|
||||
configList.add(new MybatisInterceptorConfig(OperationLogBlob.class, "modifiedValue", CompressUtils.class, "zip", "unzip"));
|
||||
// ShareInfo
|
||||
configList.add(new MybatisInterceptorConfig(ShareInfo.class, "customData", CompressUtils.class, "zipString", "unzipString"));
|
||||
|
||||
return configList;
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue