refactor(接口测试): 重构执行文件接口的响应内容处理
This commit is contained in:
parent
1a63411395
commit
f4ec65d928
|
@ -101,6 +101,11 @@ public class ResponseResult {
|
||||||
*/
|
*/
|
||||||
private long headerSize = 0;
|
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.Operation;
|
||||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||||
import jakarta.annotation.Resource;
|
import jakarta.annotation.Resource;
|
||||||
|
import jakarta.servlet.http.HttpServletResponse;
|
||||||
import org.apache.shiro.authz.annotation.Logical;
|
import org.apache.shiro.authz.annotation.Logical;
|
||||||
import org.apache.shiro.authz.annotation.RequiresPermissions;
|
import org.apache.shiro.authz.annotation.RequiresPermissions;
|
||||||
import org.springframework.validation.annotation.Validated;
|
import org.springframework.validation.annotation.Validated;
|
||||||
|
@ -133,5 +134,15 @@ public class ApiTestController {
|
||||||
return apiTestService.getPoolId(projectId);
|
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.ExtEnvironmentMapper;
|
||||||
import io.metersphere.project.mapper.ExtProjectMapper;
|
import io.metersphere.project.mapper.ExtProjectMapper;
|
||||||
import io.metersphere.project.service.EnvironmentService;
|
import io.metersphere.project.service.EnvironmentService;
|
||||||
|
import io.metersphere.project.service.FileService;
|
||||||
import io.metersphere.project.service.ProjectApplicationService;
|
import io.metersphere.project.service.ProjectApplicationService;
|
||||||
import io.metersphere.sdk.constants.ProjectApplicationType;
|
import io.metersphere.sdk.constants.ProjectApplicationType;
|
||||||
|
import io.metersphere.sdk.constants.StorageType;
|
||||||
import io.metersphere.sdk.domain.Environment;
|
import io.metersphere.sdk.domain.Environment;
|
||||||
|
import io.metersphere.sdk.file.FileRequest;
|
||||||
import io.metersphere.sdk.util.BeanUtils;
|
import io.metersphere.sdk.util.BeanUtils;
|
||||||
|
import io.metersphere.sdk.util.LogUtils;
|
||||||
import io.metersphere.system.domain.TestResourcePool;
|
import io.metersphere.system.domain.TestResourcePool;
|
||||||
import io.metersphere.system.dto.ProtocolDTO;
|
import io.metersphere.system.dto.ProtocolDTO;
|
||||||
import io.metersphere.system.service.ApiPluginService;
|
import io.metersphere.system.service.ApiPluginService;
|
||||||
import io.metersphere.system.service.PluginLoadService;
|
import io.metersphere.system.service.PluginLoadService;
|
||||||
import io.metersphere.system.utils.ServiceUtils;
|
import io.metersphere.system.utils.ServiceUtils;
|
||||||
import jakarta.annotation.Resource;
|
import jakarta.annotation.Resource;
|
||||||
|
import jakarta.servlet.http.HttpServletResponse;
|
||||||
import org.apache.commons.lang3.StringUtils;
|
import org.apache.commons.lang3.StringUtils;
|
||||||
import org.pf4j.PluginWrapper;
|
import org.pf4j.PluginWrapper;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
import org.springframework.transaction.annotation.Transactional;
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
@ -50,6 +57,8 @@ public class ApiTestService {
|
||||||
private ExtProjectMapper extProjectMapper;
|
private ExtProjectMapper extProjectMapper;
|
||||||
@Resource
|
@Resource
|
||||||
private ProjectApplicationService projectApplicationService;
|
private ProjectApplicationService projectApplicationService;
|
||||||
|
@Resource
|
||||||
|
private FileService fileService;
|
||||||
|
|
||||||
public List<ProtocolDTO> getProtocols(String orgId) {
|
public List<ProtocolDTO> getProtocols(String orgId) {
|
||||||
List<ProtocolDTO> protocols = apiPluginService.getProtocols(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());
|
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);
|
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) {
|
export function getShareTime(projectId: string) {
|
||||||
return MSR.get<string>({ url: `${reportUrl.getShareTimeUrl}/${projectId}` });
|
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 {};
|
export default {};
|
||||||
|
|
|
@ -45,3 +45,5 @@ export const reportCaseShareUrl = '/api/report/case/share';
|
||||||
export const getShareIdUrl = '/api/report/share/gen';
|
export const getShareIdUrl = '/api/report/share/gen';
|
||||||
export const getShareReportInfoUrl = '/api/report/share/get';
|
export const getShareReportInfoUrl = '/api/report/share/get';
|
||||||
export const getShareTimeUrl = '/api/report/share/get-share-time';
|
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 {
|
export interface ResponseResult {
|
||||||
body: string;
|
body: string;
|
||||||
contentType: string;
|
contentType: string;
|
||||||
|
filePath?: string;
|
||||||
headers: string;
|
headers: string;
|
||||||
dnsLookupTime: number;
|
dnsLookupTime: number;
|
||||||
downloadTime: number;
|
downloadTime: number;
|
||||||
|
|
|
@ -1,10 +1,10 @@
|
||||||
<template>
|
<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]">
|
<div class="mb-[8px] flex items-center gap-[16px]">
|
||||||
<a-button type="outline" class="arco-btn-outline--secondary" size="mini" @click="handleDownload">
|
<a-button type="outline" class="arco-btn-outline--secondary" size="mini" @click="handleDownload">
|
||||||
{{ t('common.download') }}
|
{{ t('common.download') }}
|
||||||
</a-button>
|
</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-if="isPdf" value="pdf">pdf</a-radio>
|
||||||
<a-radio v-else value="image">{{ t('common.image') }}</a-radio>
|
<a-radio v-else value="image">{{ t('common.image') }}</a-radio>
|
||||||
<a-radio value="text">{{ t('common.text') }}</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 { LanguageEnum } from '@/components/pure/ms-code-editor/types';
|
||||||
import MsIcon from '@/components/pure/ms-icon-font/index.vue';
|
import MsIcon from '@/components/pure/ms-icon-font/index.vue';
|
||||||
|
|
||||||
|
import { downloadFile } from '@/api/modules/api-test/report';
|
||||||
import { useI18n } from '@/hooks/useI18n';
|
import { useI18n } from '@/hooks/useI18n';
|
||||||
import { downloadUrlFile } from '@/utils';
|
import { downloadByteFile, downloadUrlFile } from '@/utils';
|
||||||
|
|
||||||
import { RequestResult } from '@/models/apiTest/common';
|
import { RequestResult } from '@/models/apiTest/common';
|
||||||
|
|
||||||
|
@ -90,6 +91,10 @@
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
});
|
});
|
||||||
|
const isFile = computed(() => {
|
||||||
|
const { responseResult } = props.requestResult || {};
|
||||||
|
return !!responseResult?.filePath;
|
||||||
|
});
|
||||||
const isPdf = computed(() => {
|
const isPdf = computed(() => {
|
||||||
if (props.requestResult) {
|
if (props.requestResult) {
|
||||||
return props.requestResult.responseResult.contentType === 'application/pdf';
|
return props.requestResult.responseResult.contentType === 'application/pdf';
|
||||||
|
@ -117,11 +122,17 @@
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
function handleDownload() {
|
async function handleDownload() {
|
||||||
if (isPdf.value) {
|
if (isPdf.value) {
|
||||||
downloadUrlFile(imageUrl.value, 'response.pdf');
|
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]}`);
|
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();
|
const responseEditorRef = ref();
|
||||||
|
|
Loading…
Reference in New Issue