安装
npm i mock-proxy-kit -S
团队配置
import { SmartMockRule } from "./smartMock";
export type OperationPermissions = {
sceneOpen?: boolean;
sceneAdd?: boolean;
sceneUpdate?: boolean;
sceneDelete?: boolean;
sceneNameEdit?: boolean;
defaultSceneEdit?: boolean;
apiMove?: boolean;
groupAdd?: boolean;
groupDelete?: boolean;
}
export interface ProjectConfig {
name: string;
id: string | number;
crossOrigin?: boolean;
smartMockRules?: SmartMockRule[];
[key: string]: any;
}
export interface SiteConfig {
name: string;
owners?: string[];
desc?: string;
domains: string[];
projects: ProjectConfig[];
}
export interface TeamConfig {
sites: SiteConfig[];
scriptUrl: string;
corsConfig: {
accessControlAllowHeaders?: string[];
redirectTarget: string | string[];
};
configPageUrl?: string;
operationPermissions?: OperationPermissions;
defaultSceneId?: string;
smartMockRules?: SmartMockRule[];
}
用户自定义脚本
import {
ApiMethod
} from "./common";
import { ProjectConfig } from "./teamConfig";
export interface SceneResponse {
id: string | number;
name: string;
mockData: any;
mockUrl: string;
[key: string]: any;
}
export interface AddScenePayload {
name: string;
mockData: any;
}
export interface AddSceneResponse {
id: string | number;
[key: string]: any;
}
export type OverviewApiResponse = {
id: number | string;
path: string;
realPath?: string;
regexFilter?: string;
method: ApiMethod;
name: string;
desc?: string;
creator?: string;
mockUrl: string;
sourceUrl?: string;
[key: string]: any;
}
export interface ApiResponse {
id: number | string;
path: string;
realPath?: string;
regexFilter?: string;
method: ApiMethod;
name: string;
desc?: string;
creator?: string;
mockUrl: string;
mockData: any;
sourceUrl?: string;
scenes?: SceneResponse[];
[key: string]: any;
}
export interface GroupResponse {
id: number | string;
name: string;
apis: OverviewApiResponse[];
[key: string]: any;
}
export interface ProjectResponse {
groups: GroupResponse[];
[key: string]: any;
}
interface RequestParams {
projectConfig: ProjectConfig;
projectResponse: ProjectResponse;
groupResponse: GroupResponse;
overviewApiResponse: OverviewApiResponse;
apiResponse: ApiResponse;
sceneResponse: SceneResponse;
}
export interface Context {
fetchJSON: <Response = any>(...args: Parameters<typeof fetch>) => Promise<Response>;
tabInfo: {
url: string;
userAgent: string;
}
}
export type GetProjectRequestParams = Pick<RequestParams, 'projectConfig'>;
export type GetProjectRequest<
P extends GetProjectRequestParams = GetProjectRequestParams,
R extends ProjectResponse = ProjectResponse
> = (
params: P,
context: Context
) => Promise<R>;
export type GetApiRequestParams = Pick<RequestParams, 'projectConfig' | 'projectResponse' | 'overviewApiResponse'>;
export type GetApiRequest<
P extends GetApiRequestParams = GetApiRequestParams,
R extends ApiResponse = ApiResponse
> = (params: P, context: Context) => Promise<R>;
export type MoveApiRequestParams = Pick<RequestParams, 'projectConfig' | 'projectResponse' | 'overviewApiResponse'> & {
'groupPayload': GroupResponse
};
export type MoveApiRequest<
P extends MoveApiRequestParams = MoveApiRequestParams,
R = any
> = (
params: P,
context: Context
) => Promise<R>;
export type UpdateApiSceneRequestParams = Pick<RequestParams, 'projectConfig' | 'projectResponse' | 'apiResponse' | 'sceneResponse'>;
export type UpdateApiSceneRequest<
P extends UpdateApiSceneRequestParams = UpdateApiSceneRequestParams,
R = any
> = (
params: P,
context: Context
) => Promise<R>;
export type AddApiSceneRequestParams = Pick<RequestParams, 'projectConfig' | 'projectResponse' | 'apiResponse'> & {
addScenePayload: AddScenePayload
};
export type AddApiSceneRequest<
P extends AddApiSceneRequestParams = AddApiSceneRequestParams,
R extends AddSceneResponse = AddSceneResponse,
> = (
params: P,
context: Context
) => Promise<R>;
export type DeleteApiSceneRequestParams = Pick<RequestParams, 'projectConfig' | 'projectResponse' | 'apiResponse' | 'sceneResponse'>;
export type DeleteApiSceneRequest<
P extends DeleteApiSceneRequestParams = DeleteApiSceneRequestParams,
R = any
> = (
params: P,
context: Context
) => Promise<R>;
export type AddGroupRequestParams = Pick<RequestParams, 'projectConfig' | 'projectResponse'> & {
addGroupPayload: {
name: string;
}
};
export type AddGroupRequest<
P extends AddGroupRequestParams = AddGroupRequestParams,
R = any
> = (
params: P,
context: Context
) => Promise<R>;
export type DeleteGroupRequestParams = Pick<RequestParams, 'projectConfig' | 'projectResponse' | 'groupResponse'>;
export type DeleteGroupRequest<
P extends DeleteGroupRequestParams = DeleteGroupRequestParams,
R = any
> = (
params: P,
context: Context
) => Promise<R>;
export interface UserScript {
getProject: GetProjectRequest;
getApi: GetApiRequest;
moveApi?: MoveApiRequest;
updateApiScene?: UpdateApiSceneRequest;
addApiScene?: AddApiSceneRequest;
deleteApiScene?: DeleteApiSceneRequest;
addGroup?: AddGroupRequest;
deleteGroup?: DeleteGroupRequest;
}
示例自定义脚本(oneapi)
import {
ProjectConfig,
GroupResponse,
OverviewApiResponse,
ApiResponse,
SceneResponse,
userScript,
AddSceneResponse,
GetApiRequestParams,
AddApiSceneRequestParams,
UpdateApiSceneRequestParams,
MoveApiRequestParams,
DeleteApiSceneRequestParams,
AddGroupRequestParams,
DeleteGroupRequestParams
} from 'mock-proxy-kit';
import {
OneapiOriginalQueryApiResponse,
OneapiOriginalQueryProjectResponse,
OneapiOriginalApiOverview
} from './types';
interface RequestMap {
[path: string]: string;
}
interface OneapiProjectConfig extends ProjectConfig {
requestMap?: RequestMap;
}
interface OneapiOverviewApiResponse extends OverviewApiResponse {
}
interface OneapiApiResponse extends ApiResponse {
}
interface OneapiAddSceneResponse extends AddSceneResponse {
}
interface OneapiSceneResponse extends SceneResponse {
}
const oneapiBaseUrl = 'https://oneapi.alibaba-inc.com';
const DEFAULT_SCENE_NAME = 'default';
function getRealPath(project: OneapiProjectConfig, api: OneapiOriginalApiOverview): string {
if ((api.description || '').includes('mtop')) return api.description as string;
return project.requestMap?.[api.apiName] || api.apiName;
}
function isMtopPath(realPath: string) {
return realPath.startsWith('/mtop') || realPath.startsWith('mtop');
}
function getMockUrl(project: OneapiProjectConfig, api: OneapiOriginalApiOverview, realPath: string, groupName?: string): string {
if (isMtopPath(realPath)) {
return `${oneapiBaseUrl}/mock/${project.id}/${api.apiName}${groupName ? `?_tag=${groupName}&wrapper=mtop` : '?wrapper=mtop'}`;
}
return `${oneapiBaseUrl}/mock/${project.id}/${api.apiName}${groupName ? `?_tag=${groupName}` : ''}`;
}
function getSourceUrl(project: OneapiProjectConfig, api: OneapiOriginalApiOverview): string {
return `${oneapiBaseUrl}/eapi/interface-manager?projectCode=${project.id}&apiName=${encodeURIComponent(api.apiName)}&method=${api.method}`;
}
function getApiRegExp(realPath: string) {
if (isMtopPath(realPath)) {
return `\\w+/${realPath.replace(/\./g, '\\.')}/\\d\\.\\d\/?`
}
return '';
}
export const getProject: userScript.GetProjectRequest<{ projectConfig: OneapiProjectConfig }> = (params, context) => {
const { projectConfig } = params;
return context.fetchJSON<OneapiOriginalQueryProjectResponse>(`${oneapiBaseUrl}/api/oneapi/group/query?projectCode=${projectConfig.id}`).then((res) => {
if (!res.success) throw new Error('获取项目失败');
const defaultGroup: GroupResponse = {
id: 'default',
name: '默认分组',
apis: (res.content.apis || []).map(api => {
const realPath = getRealPath(projectConfig, api);
const ret: OverviewApiResponse = {
id: api.id,
name: api.description || '',
method: api.method,
path: api.apiName,
realPath,
creator: api.creator?.nickName,
mockUrl: getMockUrl(projectConfig, api, realPath),
sourceUrl: getSourceUrl(projectConfig, api),
regexFilter: getApiRegExp(realPath),
};
return ret;
}),
}
const otherGroups: GroupResponse[] = (res.content.catalogs || []).map(group => {
const ret: GroupResponse = {
id: group.id,
name: group.name,
apis: (group.apis || []).map(api => {
const realPath = getRealPath(projectConfig, api);
const ret: OverviewApiResponse = {
id: api.id,
name: api.description || '',
method: api.method,
path: api.apiName,
realPath,
creator: api.creator?.nickName,
mockUrl: getMockUrl(projectConfig, api, realPath),
sourceUrl: getSourceUrl(projectConfig, api),
};
return ret;
}),
};
return ret;
});
const groups = [defaultGroup, ...otherGroups]
return {
groups
}
});
}
export const getApi: userScript.GetApiRequest<GetApiRequestParams & {
projectConfig: OneapiProjectConfig,
overviewApiResponse: OneapiOverviewApiResponse
}, OneapiApiResponse> = (params, context) => {
const { projectConfig, overviewApiResponse, } = params;
return context.fetchJSON<OneapiOriginalQueryApiResponse>(`${oneapiBaseUrl}/api/info/getApi?projectCode=${projectConfig.id}&apiName=${encodeURIComponent(overviewApiResponse.path)}`).then((res) => {
if (!res.success) throw new Error('获取接口失败');
const scenes: SceneResponse[] = [];
const realPath = getRealPath(projectConfig, res.content);
Object.entries(res.content.tagResponses).forEach(([groupName, mockData]) => {
scenes.push({
id: groupName,
name: groupName,
mockUrl: getMockUrl(
projectConfig,
res.content,
realPath,
groupName === DEFAULT_SCENE_NAME ? '' : groupName
),
mockData: mockData,
})
})
const ret: ApiResponse = {
id: res.content.id,
name: res.content.description || '',
method: res.content.method,
path: res.content.apiName,
realPath,
regexFilter: getApiRegExp(realPath),
creator: res.content.creator?.nickName,
mockUrl: getMockUrl(projectConfig, res.content, realPath),
sourceUrl: getSourceUrl(projectConfig, res.content),
mockData: res.content.tagResponses?.default,
scenes
};
return ret;
});
}
export const moveApi: userScript.MoveApiRequest<
MoveApiRequestParams & {
projectConfig: OneapiProjectConfig,
overviewApiResponse: OneapiOverviewApiResponse,
},
{
success: boolean
}
> = (params, context) => {
const {
projectConfig,
overviewApiResponse,
groupPayload,
} = params;
const payload: any = {
id: overviewApiResponse.id,
apiId: overviewApiResponse.id,
apiName: overviewApiResponse.path,
projectCode: projectConfig.id,
csrf: '',
};
if (groupPayload.id !== 'default') {
payload.pid = groupPayload.id;
}
return context.fetchJSON<{
success: boolean
}>(`${oneapiBaseUrl}/api/info/update`, {
method: 'POST',
body: JSON.stringify(payload),
headers: {
'Content-Type': 'application/json'
}
}).then((res) => {
if (!res.success) throw new Error('更新失败');
return res;
});
}
export const addApiScene: userScript.AddApiSceneRequest<
AddApiSceneRequestParams & {
projectConfig: OneapiProjectConfig,
apiResponse: OneapiApiResponse,
},
OneapiAddSceneResponse
> = (
params,
context
) => {
const {
projectConfig,
apiResponse,
addScenePayload,
} = params;
const payload = {
apiName: apiResponse.path,
projectCode: projectConfig.id,
tagName: addScenePayload.name,
tagResponse: addScenePayload.mockData,
csrf: '',
};
return context.fetchJSON<{
success: boolean
content: string
}>(`${oneapiBaseUrl}/api/info/createTag`, {
method: 'POST',
body: JSON.stringify(payload),
headers: {
'Content-Type': 'application/json'
}
}).then((res) => {
if (!res.success) throw new Error('创建失败');
return {
id: res.content
};
});
}
export const updateApiScene: userScript.UpdateApiSceneRequest<
UpdateApiSceneRequestParams & {
projectConfig: OneapiProjectConfig,
apiResponse: OneapiApiResponse,
sceneResponse: OneapiSceneResponse,
},
{
success: boolean
}
> = (params, context) => {
const {
projectConfig,
apiResponse,
sceneResponse,
} = params;
const payload = {
apiName: apiResponse.path,
projectCode: projectConfig.id,
tagName: sceneResponse.name,
tagResponse: sceneResponse.mockData,
csrf: '',
};
return context.fetchJSON<{
success: boolean
}>(`${oneapiBaseUrl}/api/info/updateTag`, {
method: 'POST',
body: JSON.stringify(payload),
headers: {
'Content-Type': 'application/json'
}
}).then((res) => {
if (!res.success) throw new Error('更新失败');
return res;
});
}
export const deleteApiScene: userScript.DeleteApiSceneRequest<
DeleteApiSceneRequestParams & {
projectConfig: OneapiProjectConfig,
apiResponse: OneapiApiResponse,
sceneResponse: OneapiSceneResponse,
},
{
success: boolean
}
> = (
params,
context,
) => {
const {
projectConfig,
apiResponse,
sceneResponse,
} = params;
const payload = {
apiName: apiResponse.path,
projectCode: projectConfig.id,
tagName: sceneResponse.name,
csrf: '',
};
return context.fetchJSON<{
success: boolean
}>(`${oneapiBaseUrl}/api/info/deleteTag`, {
method: 'POST',
body: JSON.stringify(payload),
headers: {
'Content-Type': 'application/json'
}
}).then((res) => {
if (!res.success) throw new Error('删除失败');
return res;
});
}
export const addGroup: userScript.AddGroupRequest<
AddGroupRequestParams & {
projectConfig: OneapiProjectConfig,
},
{
success: boolean
}
> = (
params,
context,
) => {
const {
projectConfig,
addGroupPayload
} = params;
return context.fetchJSON<{
success: boolean
}>(`${oneapiBaseUrl}/api/oneapi/group/create?name=${addGroupPayload.name}&projectCode=${projectConfig.id}`).then((res) => {
if (!res.success) throw new Error('创建失败');
return res;
});
}
export const deleteGroup: userScript.DeleteGroupRequest<
DeleteGroupRequestParams & {
projectConfig: OneapiProjectConfig,
},
{
success: boolean
}
> = (
params,
context,
) => {
const {
groupResponse
} = params;
if (groupResponse.id === 'default') {
return Promise.reject(new Error('默认分组无法删除'));
}
return context.fetchJSON<{
success: boolean
}>(`${oneapiBaseUrl}/api/oneapi/group/delete?id=${groupResponse.id}`).then((res) => {
if (!res.success) throw new Error('删除失败');
return res;
});
}