feat(接口调试): 本地执行

This commit is contained in:
baiqi 2024-02-27 16:52:17 +08:00 committed by Craftsman
parent 558dbcfff6
commit a74f9cc9e6
10 changed files with 69 additions and 26 deletions

View File

@ -9,6 +9,7 @@ import {
GetApiDebugDetailUrl, GetApiDebugDetailUrl,
GetDebugModuleCountUrl, GetDebugModuleCountUrl,
GetDebugModulesUrl, GetDebugModulesUrl,
LocalExecuteApiDebugUrl,
MoveDebugModuleUrl, MoveDebugModuleUrl,
TestMockUrl, TestMockUrl,
UpdateApiDebugUrl, UpdateApiDebugUrl,
@ -66,6 +67,11 @@ export function executeDebug(data: ExecuteRequestParams) {
return MSR.post<ExecuteRequestParams>({ url: ExecuteApiDebugUrl, data }); return MSR.post<ExecuteRequestParams>({ url: ExecuteApiDebugUrl, data });
} }
// 本地执行调试
export function localExecuteApiDebug(host: string, data: ExecuteRequestParams) {
return MSR.post<ExecuteRequestParams>({ url: `${host}${LocalExecuteApiDebugUrl}`, data });
}
// 新增调试 // 新增调试
export function addDebug(data: SaveDebugParams) { export function addDebug(data: SaveDebugParams) {
return MSR.post({ url: AddApiDebugUrl, data }); return MSR.post({ url: AddApiDebugUrl, data });

View File

@ -74,15 +74,15 @@ export function testCommonScript(data: TestScriptType) {
return MSR.post({ url: TestScriptUrl, data }); return MSR.post({ url: TestScriptUrl, data });
} }
// apiSocket 建立连接 // apiSocket 建立连接
export const apiSocket = (url: string) => { export const apiSocket = (url: string, host?: string) => {
let protocol = 'ws://'; let protocol = 'ws://';
if (window.location.protocol === 'https:') { if (window.location.protocol === 'https:' || host?.startsWith('https')) {
protocol = 'wss://'; protocol = 'wss://';
} }
const uri = protocol + window.location.host + url; const uri = protocol + (host?.split('://')[1] || window.location.host) + url;
return new WebSocket(uri); return new WebSocket(uri);
}; };
export function getSocket(reportId: string) { export function getSocket(reportId: string, socketUrl?: string, host?: string) {
return apiSocket(`${ConnectionWebsocketUrl}/${reportId}`); return apiSocket(`${socketUrl || ConnectionWebsocketUrl}/${reportId}`, host);
} }

View File

@ -85,8 +85,8 @@ export function addLocalConfig(data: AddLocalConfigParams) {
} }
// 个人设置-验证本地执行配置 // 个人设置-验证本地执行配置
export function validLocalConfig(id: string) { export function validLocalConfig(host: string) {
return MSR.get<boolean>({ url: ValidLocalConfigUrl, params: id }); return MSR.get({ url: `${host}${ValidLocalConfigUrl}` });
} }
// 个人设置-获取本地执行配置 // 个人设置-获取本地执行配置

View File

@ -1,4 +1,5 @@
export const ExecuteApiDebugUrl = '/api/debug/debug'; // 执行调试 export const ExecuteApiDebugUrl = '/api/debug/debug'; // 执行调试
export const LocalExecuteApiDebugUrl = '/api/debug'; // 本地执行调试
export const AddApiDebugUrl = '/api/debug/add'; // 新增调试 export const AddApiDebugUrl = '/api/debug/add'; // 新增调试
export const UpdateApiDebugUrl = '/api/debug/update'; // 更新调试 export const UpdateApiDebugUrl = '/api/debug/update'; // 更新调试
export const GetApiDebugDetailUrl = '/api/debug/get'; // 获取接口调试详情 export const GetApiDebugDetailUrl = '/api/debug/get'; // 获取接口调试详情

View File

@ -7,7 +7,7 @@ export const GetMenuListUrl = '/api/user/menu';
export const GetPublicKeyUrl = '/get-key'; export const GetPublicKeyUrl = '/get-key';
export const UpdateLocalConfigUrl = '/user/local/config/update'; // 个人设置-更新本地执行配置 export const UpdateLocalConfigUrl = '/user/local/config/update'; // 个人设置-更新本地执行配置
export const AddLocalConfigUrl = '/user/local/config/add'; // 个人设置-添加本地执行 export const AddLocalConfigUrl = '/user/local/config/add'; // 个人设置-添加本地执行
export const ValidLocalConfigUrl = '/user/local/config/validate'; // 个人设置-验证本地执行 export const ValidLocalConfigUrl = '/status'; // 个人设置-验证本地执行
export const GetLocalConfigUrl = '/user/local/config/get'; // 个人设置-获取本地执行配置 export const GetLocalConfigUrl = '/user/local/config/get'; // 个人设置-获取本地执行配置
export const EnableLocalConfigUrl = '/user/local/config/enable'; // 个人设置-启用本地执行配置 export const EnableLocalConfigUrl = '/user/local/config/enable'; // 个人设置-启用本地执行配置
export const DisableLocalConfigUrl = '/user/local/config/disable'; // 个人设置-禁用本地执行配置 export const DisableLocalConfigUrl = '/user/local/config/disable'; // 个人设置-禁用本地执行配置

View File

@ -49,7 +49,8 @@
</div> </div>
</div> </div>
</div> </div>
<div v-xpack class="config-card"> <!-- TODO:UI 测试暂无 -->
<!-- <div v-xpack class="config-card">
<div class="config-card-title"> <div class="config-card-title">
<div class="config-card-title-text">{{ t('ms.personal.uiLocalExecution') }}</div> <div class="config-card-title-text">{{ t('ms.personal.uiLocalExecution') }}</div>
<MsTag <MsTag
@ -93,7 +94,7 @@
/> />
</div> </div>
</div> </div>
</div> </div> -->
</div> </div>
</a-spin> </a-spin>
</template> </template>
@ -155,10 +156,13 @@
}); });
async function testApi() { async function testApi() {
if (apiConfig.value.userUrl.trim() === '') {
return;
}
try { try {
testApiLoading.value = true; testApiLoading.value = true;
const res = await validLocalConfig(apiConfig.value.id); const res = await validLocalConfig(apiConfig.value.userUrl.trim());
apiConfig.value.status = res ? 1 : 2; apiConfig.value.status = res === 'OK' ? 1 : 2;
if (res) { if (res) {
// //
if (apiConfig.value.id) { if (apiConfig.value.id) {
@ -212,6 +216,9 @@
}); });
async function testUi() { async function testUi() {
if (uiConfig.value.userUrl.trim() === '') {
return;
}
try { try {
testUiLoading.value = true; testUiLoading.value = true;
if (uiConfig.value.id) { if (uiConfig.value.id) {
@ -227,8 +234,8 @@
}); });
uiConfig.value.id = result.id; uiConfig.value.id = result.id;
} }
const res = await validLocalConfig(uiConfig.value.id); const res = await validLocalConfig(uiConfig.value.userUrl.trim());
uiConfig.value.status = res ? 1 : 2; uiConfig.value.status = res === 'OK' ? 1 : 2;
if (res) { if (res) {
Message.success(t('ms.personal.testPass')); Message.success(t('ms.personal.testPass'));
} else { } else {

View File

@ -317,6 +317,7 @@ export interface ExecuteRequestParams {
linkFileIds: string[]; linkFileIds: string[];
request: ExecuteHTTPRequestFullParams | ExecutePluginRequestParams; request: ExecuteHTTPRequestFullParams | ExecutePluginRequestParams;
projectId: string; projectId: string;
frontendDebug?: boolean; // 是否本地调试,该模式下接口会返回执行参数,用来调用本地执行服务
} }
// 保存接口调试入参 // 保存接口调试入参
export interface SaveDebugParams { export interface SaveDebugParams {

View File

@ -54,7 +54,7 @@
v-if="!requestVModel.executeLoading" v-if="!requestVModel.executeLoading"
:disabled="requestVModel.executeLoading || (isHttpProtocol && !requestVModel.url)" :disabled="requestVModel.executeLoading || (isHttpProtocol && !requestVModel.url)"
class="exec-btn" class="exec-btn"
@click="execute" @click="() => execute(isPriorityLocalExec ? 'localExec' : 'serverExec')"
@select="execute" @select="execute"
> >
{{ isPriorityLocalExec ? t('apiTestDebug.localExec') : t('apiTestDebug.serverExec') }} {{ isPriorityLocalExec ? t('apiTestDebug.localExec') : t('apiTestDebug.serverExec') }}
@ -62,7 +62,7 @@
<icon-down /> <icon-down />
</template> </template>
<template v-if="hasLocalExec" #content> <template v-if="hasLocalExec" #content>
<a-doption :value="isPriorityLocalExec ? 'localExec' : 'serverExec'"> <a-doption :value="isPriorityLocalExec ? 'serverExec' : 'localExec'">
{{ isPriorityLocalExec ? t('apiTestDebug.serverExec') : t('apiTestDebug.localExec') }} {{ isPriorityLocalExec ? t('apiTestDebug.serverExec') : t('apiTestDebug.localExec') }}
</a-doption> </a-doption>
</template> </template>
@ -325,6 +325,7 @@
isDefinition?: boolean; // isDefinition?: boolean; //
hideResponseLayoutSwitch?: boolean; // hideResponseLayoutSwitch?: boolean; //
executeApi: (...args) => Promise<any>; // executeApi: (...args) => Promise<any>; //
localExecuteApi: (...args) => Promise<any>; //
createApi: (...args) => Promise<any>; // createApi: (...args) => Promise<any>; //
updateApi: (...args) => Promise<any>; // updateApi: (...args) => Promise<any>; //
uploadTempFileApi?: (...args) => Promise<any>; // uploadTempFileApi?: (...args) => Promise<any>; //
@ -468,6 +469,7 @@
const hasLocalExec = ref(false); // api const hasLocalExec = ref(false); // api
const isPriorityLocalExec = ref(false); // const isPriorityLocalExec = ref(false); //
const localExecuteUrl = ref('');
async function initLocalConfig() { async function initLocalConfig() {
try { try {
const res = await getLocalConfig(); const res = await getLocalConfig();
@ -475,6 +477,7 @@
if (apiLocalExec) { if (apiLocalExec) {
hasLocalExec.value = true; hasLocalExec.value = true;
isPriorityLocalExec.value = apiLocalExec.enable || false; isPriorityLocalExec.value = apiLocalExec.enable || false;
localExecuteUrl.value = apiLocalExec.userUrl || '';
} }
} catch (error) { } catch (error) {
// eslint-disable-next-line no-console // eslint-disable-next-line no-console
@ -656,8 +659,12 @@
const reportId = ref(''); const reportId = ref('');
const websocket = ref<WebSocket>(); const websocket = ref<WebSocket>();
function debugSocket() { function debugSocket(executeType?: 'localExec' | 'serverExec') {
websocket.value = getSocket(reportId.value); websocket.value = getSocket(
reportId.value,
executeType === 'localExec' ? '/ws/debug' : '',
executeType === 'localExec' ? localExecuteUrl.value : ''
);
websocket.value.addEventListener('message', (event) => { websocket.value.addEventListener('message', (event) => {
const data = JSON.parse(event.data); const data = JSON.parse(event.data);
if (data.msgType === 'EXEC_RESULT') { if (data.msgType === 'EXEC_RESULT') {
@ -700,7 +707,7 @@
} }
); );
function makeRequestParams() { function makeRequestParams(executeType?: 'localExec' | 'serverExec') {
const { formDataBody, wwwFormBody } = requestVModel.value.body; const { formDataBody, wwwFormBody } = requestVModel.value.body;
const polymorphicName = protocolOptions.value.find( const polymorphicName = protocolOptions.value.find(
(e) => e.value === requestVModel.value.protocol (e) => e.value === requestVModel.value.protocol
@ -743,7 +750,7 @@
} }
reportId.value = getGenerateId(); reportId.value = getGenerateId();
requestVModel.value.reportId = reportId.value; // ID requestVModel.value.reportId = reportId.value; // ID
debugSocket(); // websocket debugSocket(executeType); // websocket
return { return {
id: requestVModel.value.id.toString(), id: requestVModel.value.id.toString(),
reportId: reportId.value, reportId: reportId.value,
@ -768,6 +775,7 @@
}, },
...parseRequestBodyResult, ...parseRequestBodyResult,
projectId: appStore.currentProjectId, projectId: appStore.currentProjectId,
frontendDebug: executeType === 'localExec',
}; };
} }
@ -775,12 +783,14 @@
* 执行调试 * 执行调试
* @param val 执行类型 * @param val 执行类型
*/ */
async function execute(execuetType?: 'localExec' | 'serverExec') { async function execute(executeType?: 'localExec' | 'serverExec') {
// TODO:&
if (isHttpProtocol.value) { if (isHttpProtocol.value) {
try { try {
requestVModel.value.executeLoading = true; requestVModel.value.executeLoading = true;
await props.executeApi(makeRequestParams()); const res = await props.executeApi(makeRequestParams(executeType));
if (executeType === 'localExec') {
await props.localExecuteApi(localExecuteUrl.value, res);
}
} catch (error) { } catch (error) {
// eslint-disable-next-line no-console // eslint-disable-next-line no-console
console.log(error); console.log(error);
@ -792,7 +802,10 @@
if (valid === true) { if (valid === true) {
try { try {
requestVModel.value.executeLoading = true; requestVModel.value.executeLoading = true;
await props.executeApi(makeRequestParams()); const res = await props.executeApi(makeRequestParams(executeType));
if (executeType === 'localExec') {
await props.localExecuteApi(localExecuteUrl.value, res);
}
} catch (error) { } catch (error) {
// eslint-disable-next-line no-console // eslint-disable-next-line no-console
console.log(error); console.log(error);

View File

@ -38,6 +38,7 @@
:create-api="addDebug" :create-api="addDebug"
:update-api="updateDebug" :update-api="updateDebug"
:execute-api="executeDebug" :execute-api="executeDebug"
:local-execute-api="localExecuteApiDebug"
:upload-temp-file-api="uploadTempFile" :upload-temp-file-api="uploadTempFile"
@add-done="handleDebugAddDone" @add-done="handleDebugAddDone"
/> />
@ -93,7 +94,14 @@
import apiMethodName from '@/views/api-test/components/apiMethodName.vue'; import apiMethodName from '@/views/api-test/components/apiMethodName.vue';
import debug, { RequestParam } from '@/views/api-test/components/requestComposition/index.vue'; import debug, { RequestParam } from '@/views/api-test/components/requestComposition/index.vue';
import { addDebug, executeDebug, getDebugDetail, updateDebug, uploadTempFile } from '@/api/modules/api-test/debug'; import {
addDebug,
executeDebug,
getDebugDetail,
localExecuteApiDebug,
updateDebug,
uploadTempFile,
} from '@/api/modules/api-test/debug';
import { useI18n } from '@/hooks/useI18n'; import { useI18n } from '@/hooks/useI18n';
import useModal from '@/hooks/useModal'; import useModal from '@/hooks/useModal';
import { parseCurlScript } from '@/utils'; import { parseCurlScript } from '@/utils';

View File

@ -24,6 +24,7 @@
:create-api="addDebug" :create-api="addDebug"
:update-api="updateDebug" :update-api="updateDebug"
:execute-api="executeDebug" :execute-api="executeDebug"
:local-execute-api="localExecuteApiDebug"
is-definiton is-definiton
@add-done="emit('addDone')" @add-done="emit('addDone')"
/> />
@ -84,7 +85,13 @@
import apiTable from './apiTable.vue'; import apiTable from './apiTable.vue';
import apiMethodName from '@/views/api-test/components/apiMethodName.vue'; import apiMethodName from '@/views/api-test/components/apiMethodName.vue';
import { addDebug, executeDebug, getDebugDetail, updateDebug } from '@/api/modules/api-test/debug'; import {
addDebug,
executeDebug,
getDebugDetail,
localExecuteApiDebug,
updateDebug,
} from '@/api/modules/api-test/debug';
import { useI18n } from '@/hooks/useI18n'; import { useI18n } from '@/hooks/useI18n';
import { ExecuteBody } from '@/models/apiTest/debug'; import { ExecuteBody } from '@/models/apiTest/debug';