feat(接口测试): mock页面&部分接口联调

This commit is contained in:
WangXu10 2024-03-07 20:17:09 +08:00 committed by Craftsman
parent e05341c4e0
commit f51eb51956
9 changed files with 475 additions and 5 deletions

View File

@ -31,7 +31,6 @@ public class ApiDefinitionMockPageRequest extends BasePageRequest {
private boolean enable = true;
@Schema(description = "接口fk", requiredMode = Schema.RequiredMode.REQUIRED)
@NotBlank(message = "{api_definition_mock.api_definition_id.not_blank}")
@Size(min = 1, max = 50, message = "{api_definition_mock.api_definition_id.length_range}")
private String apiDefinitionId;

View File

@ -9,12 +9,14 @@
<select id="list" resultMap="ApiDefinitionMockDTO">
select
m.id, m.create_time, m.update_time, m.create_user, m.`name`, m.tags, m.`enable`, m.expect_num, m.project_id,
m.api_definition_id, u.name as create_user_name
m.api_definition_id, u.name as create_user_name, d.path as api_path
from api_definition_mock m
left join `api_definition` d on d.id = m.api_definition_id
left join `user` u on u.id = m.create_user
where m.api_definition_id = #{request.apiDefinitionId}
<where>
<include refid="queryWhereCondition"/>
</where>
</select>
<select id="getIdsByApiIds" resultType="java.lang.String">
select
@ -39,6 +41,9 @@
<if test="request.projectId != null and request.projectId != ''">
and m.project_id = #{request.projectId}
</if>
<if test="request.apiDefinitionId != null and request.apiDefinitionId != ''">
and m.api_definition_id = #{request.apiDefinitionId}
</if>
<include refid="filters">
<property name="filter" value="request.filter"/>

View File

@ -3,8 +3,10 @@ import {
AddDefinitionUrl,
AddModuleUrl,
BatchDeleteDefinitionUrl,
DefinitionMockPageUrl,
DefinitionPageUrl,
DeleteDefinitionUrl,
DeleteMockUrl,
DeleteModuleUrl,
GetDefinitionDetailUrl,
GetEnvModuleUrl,
@ -15,6 +17,7 @@ import {
TransferFileModuleOptionUrl,
TransferFileUrl,
UpdateDefinitionUrl,
UpdateMockStatusUrl,
UpdateModuleUrl,
UploadTempFileUrl,
} from '@/api/requrls/api-test/management';
@ -24,10 +27,13 @@ import {
ApiDefinitionDetail,
ApiDefinitionGetEnvModuleParams,
ApiDefinitionGetModuleParams,
ApiDefinitionMockDetail,
ApiDefinitionMockPageParams,
ApiDefinitionPageParams,
ApiDefinitionUpdateModuleParams,
ApiDefinitionUpdateParams,
EnvModule,
mockParams,
} from '@/models/apiTest/management';
import { AddModuleParams, CommonList, ModuleTreeNode, MoveModules, TransferFileParams } from '@/models/common';
@ -115,3 +121,21 @@ export function deleteDefinition(id: string) {
export function batchDeleteDefinition(id: string) {
return MSR.get({ url: BatchDeleteDefinitionUrl, params: id });
}
/**
* Mock
*/
// 获取mock列表接口
export function getDefinitionMockPage(data: ApiDefinitionMockPageParams) {
return MSR.post<CommonList<ApiDefinitionMockDetail>>({ url: DefinitionMockPageUrl, data });
}
// 更新mock状态接口
export function updateMockStatusPage(id: string) {
return MSR.get({ url: UpdateMockStatusUrl, params: id });
}
// 刪除mock接口
export function deleteDefinitionMockMock(data: mockParams) {
return MSR.post({ url: DeleteMockUrl, data });
}

View File

@ -15,3 +15,6 @@ export const TransferFileModuleOptionUrl = '/api/definition/transfer/options'; /
export const UploadTempFileUrl = '/api/definition/upload/temp/file'; // 临时文件上传
export const DeleteDefinitionUrl = '/api/definition/delete'; // 删除接口定义
export const BatchDeleteDefinitionUrl = '/api/definition/batch-del'; // 批量删除接口定义
export const DefinitionMockPageUrl = '/api/definition/mock/page'; // mock列表
export const UpdateMockStatusUrl = '/api/definition/mock/enable/'; // 更新mock状态
export const DeleteMockUrl = '/api/definition/mock/delete'; // 刪除mock

View File

@ -7,6 +7,7 @@ export interface ApiDefinitionCustomField {
fieldId: string;
value: string;
}
// 创建定义参数
export interface ApiDefinitionCreateParams extends ExecuteRequestParams {
tags: string[];
@ -16,14 +17,17 @@ export interface ApiDefinitionCreateParams extends ExecuteRequestParams {
customFields: ApiDefinitionCustomField[];
moduleId: string;
versionId: string;
[key: string]: any; // 其他前端定义的参数
}
// 更新定义参数
export interface ApiDefinitionUpdateParams extends ApiDefinitionCreateParams {
id: string;
deleteFileIds: string[];
unLinkFileIds: string[];
}
// 定义-自定义字段详情
export interface ApiDefinitionCustomFieldDetail {
id: string;
@ -43,6 +47,7 @@ export interface ApiDefinitionCustomFieldDetail {
apiId: string;
fieldId: string;
}
// 定义详情
export interface ApiDefinitionDetail extends ApiDefinitionCreateParams {
id: string;
@ -74,11 +79,13 @@ export interface ApiDefinitionDetail extends ApiDefinitionCreateParams {
follow: boolean;
customFields: ApiDefinitionCustomFieldDetail[];
}
// 定义-更新模块参数
export interface ApiDefinitionUpdateModuleParams {
id: string;
name: string;
}
// 定义-获取模块树参数
export interface ApiDefinitionGetModuleParams {
keyword: string;
@ -91,6 +98,7 @@ export interface ApiDefinitionGetModuleParams {
versionId?: string;
refId?: string;
}
// 环境-选中的模块
export interface SelectedModule {
// 选中的模块
@ -98,16 +106,19 @@ export interface SelectedModule {
containChildModule: boolean; // 是否包含新增子模块
disabled: boolean;
}
// 定义-获取环境的模块树参数
export interface ApiDefinitionGetEnvModuleParams {
projectId: string;
selectedModules?: SelectedModule[];
}
// 环境-模块树
export interface EnvModule {
moduleTree: ModuleTreeNode[];
selectedModules: SelectedModule[];
}
// 定义列表查询参数
export interface ApiDefinitionPageParams extends TableQueryParams {
id: string;
@ -119,3 +130,37 @@ export interface ApiDefinitionPageParams extends TableQueryParams {
moduleIds: string[];
deleted: boolean;
}
// mock列表请求参数
export interface ApiDefinitionMockPageParams extends TableQueryParams {
id: string;
name: string;
projectId: string;
enable: boolean;
apiDefinitionId: string;
}
export interface ApiDefinitionMockDetail {
id: string;
name: string;
tags: string[];
enable: boolean;
expectNum: string;
projectId: string;
apiDefinitionId: string;
createTime: number;
updateTime: number;
createUser: string;
matching: string;
response: string;
createUserName: string;
apiNum: string;
apiName: string;
apiPath: string;
apiMethod: string;
}
export interface mockParams {
id: string;
projectId: string;
}

View File

@ -10,7 +10,15 @@
/>
</a-tab-pane>
<a-tab-pane key="case" title="CASE" class="ms-api-tab-pane"> </a-tab-pane>
<a-tab-pane key="mock" title="MOCK" class="ms-api-tab-pane"> </a-tab-pane>
<a-tab-pane key="mock" title="MOCK" class="ms-api-tab-pane">
<mock-table
ref="mockRef"
:module-tree="props.moduleTree"
:active-module="props.activeModule"
:offspring-ids="props.offspringIds"
:protocol="protocol"
/>
</a-tab-pane>
<!-- <a-tab-pane key="doc" title="API Docs" class="ms-api-tab-pane"> </a-tab-pane> -->
<template #extra>
<div class="flex items-center gap-[8px] pr-[24px]">
@ -35,6 +43,7 @@
<script setup lang="ts">
import MsSelect from '@/components/business/ms-select';
import api from './api/index.vue';
import MockTable from '@/views/api-test/management/components/management/mock/mockTable.vue';
import { useI18n } from '@/hooks/useI18n';

View File

@ -0,0 +1,367 @@
<template>
<div :class="['p-[16px_22px]', props.class]">
<div class="mb-[16px] flex items-center justify-end">
<div class="flex items-center gap-[8px]">
<a-input-search
v-model:model-value="keyword"
:placeholder="t('apiTestManagement.searchPlaceholder')"
allow-clear
class="mr-[8px] w-[240px]"
@search="loadMockList"
@press-enter="loadMockList"
/>
</div>
</div>
<ms-base-table
v-bind="propsRes"
:action-config="batchActions"
:first-column-width="44"
no-disable
filter-icon-align-left
v-on="propsEvent"
@selected-change="handleTableSelect"
@batch-action="handleTableBatch"
>
<template #action="{ record }">
<a-switch
v-model="record.enable"
size="small"
type="line"
@change="(value) => changeDefault(value, record)"
></a-switch>
<a-divider direction="vertical" :margin="8"></a-divider>
<MsTableMoreAction :list="tableMoreActionList" @select="handleTableMoreActionSelect($event, record)" />
</template>
</ms-base-table>
</div>
</template>
<script setup lang="ts">
import { FormInstance, Message } from '@arco-design/web-vue';
import dayjs from 'dayjs';
import MsBaseTable from '@/components/pure/ms-table/base-table.vue';
import type { BatchActionParams, BatchActionQueryParams, MsTableColumn } from '@/components/pure/ms-table/type';
import useTable from '@/components/pure/ms-table/useTable';
import MsTableMoreAction from '@/components/pure/ms-table-more-action/index.vue';
import { ActionsItem } from '@/components/pure/ms-table-more-action/types';
import {
deleteDefinitionMockMock,
getDefinitionMockPage,
updateMockStatusPage,
} from '@/api/modules/api-test/management';
import { useI18n } from '@/hooks/useI18n';
import useModal from '@/hooks/useModal';
import useTableStore from '@/hooks/useTableStore';
import useAppStore from '@/store/modules/app';
import { ApiDefinitionMockDetail } from '@/models/apiTest/management';
import { OrdTemplateManagement } from '@/models/setting/template';
import { TableKeyEnum } from '@/enums/tableEnum';
const props = defineProps<{
class?: string;
activeModule: string;
offspringIds: string[];
protocol: string; //
readOnly?: boolean; //
}>();
const emit = defineEmits<{
(e: 'init', params: any): void;
(e: 'change'): void;
}>();
const appStore = useAppStore();
const { t } = useI18n();
const { openModal } = useModal();
const showSubdirectory = ref(false);
const checkedEnv = ref('DEV');
const keyword = ref('');
let columns: MsTableColumn = [
{
title: 'ID',
dataIndex: 'id',
slotName: 'id',
sortIndex: 1,
sortable: {
sortDirections: ['ascend', 'descend'],
sorter: true,
},
fixed: 'left',
width: 100,
},
{
title: 'mockManagement.name',
dataIndex: 'name',
showTooltip: true,
sortable: {
sortDirections: ['ascend', 'descend'],
sorter: true,
},
width: 200,
},
{
title: 'common.tag',
dataIndex: 'tags',
isTag: true,
isStringTag: true,
width: 150,
},
{
title: 'mockManagement.apiPath',
dataIndex: 'apiPath',
slotName: 'apiPath',
showTooltip: true,
width: 200,
},
{
title: 'mockManagement.operationUser',
slotName: 'createUserName',
dataIndex: 'createUserName',
width: 200,
showDrag: true,
},
{
title: 'mockManagement.updateTime',
dataIndex: 'updateTime',
sortable: {
sortDirections: ['ascend', 'descend'],
sorter: true,
},
width: 180,
},
{
title: 'common.operation',
slotName: 'action',
dataIndex: 'operation',
fixed: 'right',
width: 150,
},
];
const { propsRes, propsEvent, loadList, setLoadListParams, resetSelector } = useTable(
getDefinitionMockPage,
{
columns: props.readOnly ? columns : [],
scroll: { x: '100%' },
tableKey: props.readOnly ? undefined : TableKeyEnum.API_TEST,
showSetting: !props.readOnly,
selectable: true,
showSelectAll: !props.readOnly,
draggable: props.readOnly ? undefined : { type: 'handle', width: 32 },
},
(item) => ({
...item,
updateTime: dayjs(item.updateTime).format('YYYY-MM-DD HH:mm:ss'),
})
);
const batchActions = {
baseAction: [
{
label: 'mockManagement.batchEnable',
eventTag: 'batchEnable',
},
{
label: 'mockManagement.batchDisEnable',
eventTag: 'batchDisEnable',
},
],
moreAction: [
{
label: 'common.delete',
eventTag: 'delete',
danger: true,
},
],
};
const tableMoreActionList = [
{
eventTag: 'copyMock',
label: t('mockManagement.copyMock'),
danger: false,
},
{
eventTag: 'delete',
label: t('common.delete'),
danger: true,
},
];
const tableQueryParams = ref<any>();
function loadMockList() {
const params = {
keyword: keyword.value,
projectId: appStore.currentProjectId,
filter: {},
};
setLoadListParams(params);
loadList();
tableQueryParams.value = {
...params,
current: propsRes.value.msPagination?.current,
pageSize: propsRes.value.msPagination?.pageSize,
};
emit('init', {
...tableQueryParams.value,
});
}
watch(
() => props.activeModule,
() => {
loadMockList();
}
);
watch(
() => props.protocol,
() => {
loadMockList();
}
);
const changeDefault = async (value: any, record: OrdTemplateManagement) => {
try {
await updateMockStatusPage(record.id);
Message.success(t('system.orgTemplate.setSuccessfully'));
loadMockList();
} catch (error) {
console.log(error);
}
};
onBeforeMount(() => {
loadMockList();
});
const tableSelected = ref<(string | number)[]>([]);
const batchParams = ref<BatchActionQueryParams>({
selectedIds: [],
selectAll: false,
excludeIds: [],
currentSelectCount: 0,
});
/**
* 删除接口
*/
function deleteMock(record?: ApiDefinitionMockDetail, isBatch?: boolean, params?: BatchActionQueryParams) {
let title = t('apiTestManagement.deleteApiTipTitle', { name: record?.name });
let selectIds = [record?.id || ''];
if (isBatch) {
title = t('apiTestManagement.batchDeleteMockTip', {
count: params?.currentSelectCount || tableSelected.value.length,
});
selectIds = tableSelected.value as string[];
}
openModal({
type: 'error',
title,
content: t('apiTestManagement.deleteMockTip'),
okText: t('common.confirmDelete'),
cancelText: t('common.cancel'),
okButtonProps: {
status: 'danger',
},
maskClosable: false,
onBeforeOk: async () => {
try {
if (isBatch) {
// await batchDeleteMock({
// selectIds,
// selectAll: !!params?.selectAll,
// excludeIds: params?.excludeIds || [],
// condition: { keyword: keyword.value },
// projectId: appStore.currentProjectId,
// });
} else {
await deleteDefinitionMockMock({
id: record?.id as string,
projectId: appStore.currentProjectId,
});
}
Message.success(t('common.deleteSuccess'));
resetSelector();
loadMockList();
} catch (error) {
// eslint-disable-next-line no-console
console.log(error);
}
},
hideCancel: false,
});
}
/**
* 处理表格更多按钮事件
* @param item
*/
function handleTableMoreActionSelect(item: ActionsItem, record: ApiDefinitionMockDetail) {
switch (item.eventTag) {
case 'delete':
deleteMock(record);
break;
default:
break;
}
}
/**
* 处理表格选中
*/
function handleTableSelect(arr: (string | number)[]) {
tableSelected.value = arr;
}
/**
* 处理表格选中后批量操作
* @param event 批量操作事件对象
*/
function handleTableBatch(event: BatchActionParams, params: BatchActionQueryParams) {
tableSelected.value = params?.selectedIds || [];
batchParams.value = params;
switch (event.eventTag) {
case 'delete':
deleteMock(undefined, true, params);
break;
default:
break;
}
}
defineExpose({
loadMockList,
});
if (!props.readOnly) {
const tableStore = useTableStore();
await tableStore.initColumn(TableKeyEnum.API_TEST, columns, 'drawer');
} else {
columns = columns.filter(
(item) => !['version', 'createTime', 'updateTime', 'operation'].includes(item.dataIndex as string)
);
}
</script>
<style lang="less" scoped>
:deep(.param-input:not(.arco-input-focus, .arco-select-view-focus)) {
&:not(:hover) {
border-color: transparent !important;
.arco-input::placeholder {
@apply invisible;
}
.arco-select-view-icon {
@apply invisible;
}
.arco-select-view-value {
color: var(--color-text-brand);
}
}
}
</style>

View File

@ -100,4 +100,13 @@ export default {
'apiTestManagement.collapseApi': 'Hide all requests',
'apiTestManagement.paramName': 'Parameter name',
'apiTestManagement.paramVal': 'Parameter value',
'mockManagement.name': 'Expected name',
'mockManagement.apiPath': 'Interface path',
'mockManagement.operationUser': 'Operator',
'mockManagement.updateTime': 'Update time',
'mockManagement.copyMock': 'Copy mock address',
'mockManagement.batchEnable': 'Batch enable',
'mockManagement.batchDisEnable': 'Batch disable',
'mockManagement.batchDeleteMockTip': 'Are you sure you want to delete the selected {count} mocks?',
'apiTestManagement.deleteMockTip': 'After deletion, it cannot be restored. Are you sure you want to delete it?',
};

View File

@ -94,4 +94,13 @@ export default {
'apiTestManagement.collapseApi': '隐藏全部请求',
'apiTestManagement.paramName': '参数名',
'apiTestManagement.paramVal': '参数值',
'mockManagement.name': '期望名称',
'mockManagement.apiPath': '接口路径',
'mockManagement.operationUser': '操作人',
'mockManagement.updateTime': '更新时间',
'mockManagement.copyMock': '复制Mock地址',
'mockManagement.batchEnable': '批量启用',
'mockManagement.batchDisEnable': '批量禁用',
'mockManagement.batchDeleteMockTip': '确认删除已选中的 {count} 个Mock吗',
'apiTestManagement.deleteMockTip': '刪除后將不可恢復,确认刪除嗎?',
};