refactor(接口测试): 重构执行文件接口的响应内容处理
This commit is contained in:
parent
1a63411395
commit
f4ec65d928
|
@ -101,6 +101,11 @@ public class ResponseResult {
|
|||
*/
|
||||
private long headerSize = 0;
|
||||
|
||||
/**
|
||||
* 文件地址
|
||||
*/
|
||||
private String filePath;
|
||||
|
||||
/**
|
||||
* 断言结果
|
||||
*/
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
|
@ -245,4 +245,9 @@ public class ApiTestControllerTests extends BaseTest {
|
|||
projectTestResourcePoolMapper.batchInsert(projectTestResourcePools);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void fileDownloadTestSuccess() throws Exception {
|
||||
this.requestPostAndReturn(BASE_PATH + "download", "test");
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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 {};
|
||||
|
|
|
@ -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';
|
||||
|
|
|
@ -389,6 +389,7 @@ export interface ExecuteRequestParams {
|
|||
export interface ResponseResult {
|
||||
body: string;
|
||||
contentType: string;
|
||||
filePath?: string;
|
||||
headers: string;
|
||||
dnsLookupTime: number;
|
||||
downloadTime: number;
|
||||
|
|
|
@ -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();
|
||||
|
|
Loading…
Reference in New Issue