refactor(接口测试): 重构执行文件接口的响应内容处理

This commit is contained in:
fit2-zhao 2024-05-09 13:53:49 +08:00 committed by Craftsman
parent 1a63411395
commit f4ec65d928
8 changed files with 83 additions and 6 deletions

View File

@ -101,6 +101,11 @@ public class ResponseResult {
*/
private long headerSize = 0;
/**
* 文件地址
*/
private String filePath;
/**
* 断言结果
*/

View File

@ -17,6 +17,7 @@ import io.metersphere.system.security.CheckOwner;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.annotation.Resource;
import jakarta.servlet.http.HttpServletResponse;
import org.apache.shiro.authz.annotation.Logical;
import org.apache.shiro.authz.annotation.RequiresPermissions;
import org.springframework.validation.annotation.Validated;
@ -133,5 +134,15 @@ public class ApiTestController {
return apiTestService.getPoolId(projectId);
}
@PostMapping("/download")
@Operation(summary = "执行结果附件下载")
@RequiresPermissions(value = {
PermissionConstants.PROJECT_API_SCENARIO_EXECUTE,
PermissionConstants.PROJECT_API_DEFINITION_CASE_EXECUTE,
PermissionConstants.PROJECT_API_DEBUG_EXECUTE,
PermissionConstants.PROJECT_API_REPORT_READ,
}, logical = Logical.OR)
public void download(@RequestBody TextNode path, HttpServletResponse response) throws Exception {
apiTestService.download(path.asText(), response);
}
}

View File

@ -11,21 +11,28 @@ import io.metersphere.project.dto.environment.EnvironmentConfig;
import io.metersphere.project.mapper.ExtEnvironmentMapper;
import io.metersphere.project.mapper.ExtProjectMapper;
import io.metersphere.project.service.EnvironmentService;
import io.metersphere.project.service.FileService;
import io.metersphere.project.service.ProjectApplicationService;
import io.metersphere.sdk.constants.ProjectApplicationType;
import io.metersphere.sdk.constants.StorageType;
import io.metersphere.sdk.domain.Environment;
import io.metersphere.sdk.file.FileRequest;
import io.metersphere.sdk.util.BeanUtils;
import io.metersphere.sdk.util.LogUtils;
import io.metersphere.system.domain.TestResourcePool;
import io.metersphere.system.dto.ProtocolDTO;
import io.metersphere.system.service.ApiPluginService;
import io.metersphere.system.service.PluginLoadService;
import io.metersphere.system.utils.ServiceUtils;
import jakarta.annotation.Resource;
import jakarta.servlet.http.HttpServletResponse;
import org.apache.commons.lang3.StringUtils;
import org.pf4j.PluginWrapper;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@ -50,6 +57,8 @@ public class ApiTestService {
private ExtProjectMapper extProjectMapper;
@Resource
private ProjectApplicationService projectApplicationService;
@Resource
private FileService fileService;
public List<ProtocolDTO> getProtocols(String orgId) {
List<ProtocolDTO> protocols = apiPluginService.getProtocols(orgId);
@ -122,4 +131,31 @@ public class ApiTestService {
}
return (String) configMap.get(ProjectApplicationType.API.API_RESOURCE_POOL_ID.name());
}
public void download(String path, HttpServletResponse response) {
if (StringUtils.isBlank(path)) {
return;
}
try {
String fileName = path.substring(path.lastIndexOf("/") + 1);
String folder = path.substring(0, path.lastIndexOf("/"));
FileRequest fileRequest = new FileRequest();
fileRequest.setFileName(fileName);
fileRequest.setFolder(folder);
fileRequest.setStorage(StorageType.MINIO.name());
byte[] bytes = fileService.download(fileRequest);
fileWithResponse(bytes, fileName, response);
} catch (Exception e) {
LogUtils.error(e);
}
}
private void fileWithResponse(byte[] content, String fileName, HttpServletResponse response) throws IOException {
response.setContentType("application/octet-stream");
response.setCharacterEncoding(StandardCharsets.UTF_8.name());
response.setHeader("Content-disposition", "attachment;filename=" + fileName);
response.getOutputStream().write(content);
response.getOutputStream().flush();
}
}

View File

@ -245,4 +245,9 @@ public class ApiTestControllerTests extends BaseTest {
projectTestResourcePoolMapper.batchInsert(projectTestResourcePools);
}
@Test
public void fileDownloadTestSuccess() throws Exception {
this.requestPostAndReturn(BASE_PATH + "download", "test");
}
}

View File

@ -105,4 +105,10 @@ export function getShareReportInfo(shareId: string) {
export function getShareTime(projectId: string) {
return MSR.get<string>({ url: `${reportUrl.getShareTimeUrl}/${projectId}` });
}
// 下载文件
export function downloadFile(data: string | undefined) {
return MSR.post({ url: reportUrl.DownloadFileUrl, data, responseType: 'blob' }, { isTransformResponse: false });
}
export default {};

View File

@ -45,3 +45,5 @@ export const reportCaseShareUrl = '/api/report/case/share';
export const getShareIdUrl = '/api/report/share/gen';
export const getShareReportInfoUrl = '/api/report/share/get';
export const getShareTimeUrl = '/api/report/share/get-share-time';
// 下载文件地址
export const DownloadFileUrl = '/api/test/download';

View File

@ -389,6 +389,7 @@ export interface ExecuteRequestParams {
export interface ResponseResult {
body: string;
contentType: string;
filePath?: string;
headers: string;
dnsLookupTime: number;
downloadTime: number;

View File

@ -1,10 +1,10 @@
<template>
<div v-if="showImg || isPdf" :class="showType === 'text' ? '' : 'h-full'">
<div v-if="showImg || isPdf || isFile" :class="showType === 'text' ? '' : 'h-full'">
<div class="mb-[8px] flex items-center gap-[16px]">
<a-button type="outline" class="arco-btn-outline--secondary" size="mini" @click="handleDownload">
{{ t('common.download') }}
</a-button>
<a-radio-group v-model:model-value="showType" type="button" size="small">
<a-radio-group v-model:model-value="showType" type="button" size="small" :disabled="isFile">
<a-radio v-if="isPdf" value="pdf">pdf</a-radio>
<a-radio v-else value="image">{{ t('common.image') }}</a-radio>
<a-radio value="text">{{ t('common.text') }}</a-radio>
@ -49,8 +49,9 @@
import { LanguageEnum } from '@/components/pure/ms-code-editor/types';
import MsIcon from '@/components/pure/ms-icon-font/index.vue';
import { downloadFile } from '@/api/modules/api-test/report';
import { useI18n } from '@/hooks/useI18n';
import { downloadUrlFile } from '@/utils';
import { downloadByteFile, downloadUrlFile } from '@/utils';
import { RequestResult } from '@/models/apiTest/common';
@ -90,6 +91,10 @@
}
return false;
});
const isFile = computed(() => {
const { responseResult } = props.requestResult || {};
return !!responseResult?.filePath;
});
const isPdf = computed(() => {
if (props.requestResult) {
return props.requestResult.responseResult.contentType === 'application/pdf';
@ -117,11 +122,17 @@
}
});
function handleDownload() {
async function handleDownload() {
if (isPdf.value) {
downloadUrlFile(imageUrl.value, 'response.pdf');
} else if (imageUrl.value) {
} else if (imageUrl.value && !isFile.value) {
downloadUrlFile(imageUrl.value, `response.${props.requestResult?.responseResult.contentType.split('/')[1]}`);
} else {
const res = await downloadFile(props.requestResult?.responseResult.filePath);
const path = props.requestResult?.responseResult.filePath;
const fileName = path?.substring(path.lastIndexOf('/') + 1);
downloadByteFile(res, fileName || 'response.zip');
}
}
const responseEditorRef = ref();