feat: manually mirror opencoze's code from bytedance
Change-Id: I09a73aadda978ad9511264a756b2ce51f5761adf
This commit is contained in:
@@ -0,0 +1,104 @@
|
||||
/*
|
||||
* Copyright 2025 coze-dev Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { create } from 'zustand';
|
||||
import { produce } from 'immer';
|
||||
import {
|
||||
type BizCtx,
|
||||
type MockSet,
|
||||
type MockSetBinding,
|
||||
} from '@coze-arch/bot-api/debugger_api';
|
||||
import { type BasicMockSetInfo } from '@coze-studio/mockset-shared';
|
||||
|
||||
import { isCurrent } from '../../util';
|
||||
|
||||
export interface EnabledMockSetInfo {
|
||||
mockSetBinding: MockSetBinding;
|
||||
mockSetDetail?: MockSet;
|
||||
}
|
||||
|
||||
interface MockInfoStoreState {
|
||||
bizCtx: BizCtx;
|
||||
enabledMockSetInfo: Array<EnabledMockSetInfo>;
|
||||
isPolling: boolean;
|
||||
isLoading: boolean;
|
||||
currentMockComp: Array<BasicMockSetInfo>;
|
||||
timer?: NodeJS.Timeout;
|
||||
restartTimer?: NodeJS.Timeout;
|
||||
}
|
||||
|
||||
interface MockInfoStoreAction {
|
||||
setPolling: (polling: boolean) => void;
|
||||
setLoading: (loading: boolean) => void;
|
||||
setCurrentBizCtx: (bizCtx: BizCtx) => void;
|
||||
setEnabledMockSetInfo: (mockSetList?: Array<EnabledMockSetInfo>) => void;
|
||||
removeMockComp: (mockComp: BasicMockSetInfo) => number;
|
||||
addMockComp: (mockComp: BasicMockSetInfo) => number;
|
||||
setTimer: (timer?: NodeJS.Timeout) => void;
|
||||
setRestartTimer: (timer?: NodeJS.Timeout) => void;
|
||||
}
|
||||
|
||||
export const useMockInfoStore = create<
|
||||
MockInfoStoreState & MockInfoStoreAction
|
||||
>((set, get) => ({
|
||||
bizCtx: {},
|
||||
enabledMockSetInfo: [],
|
||||
isPolling: false,
|
||||
isLoading: false,
|
||||
currentMockComp: [],
|
||||
setPolling: polling => {
|
||||
set({ isPolling: polling });
|
||||
},
|
||||
setLoading: loading => {
|
||||
set({ isLoading: loading });
|
||||
},
|
||||
setCurrentBizCtx: bizCtx => {
|
||||
set({ bizCtx });
|
||||
},
|
||||
setEnabledMockSetInfo: enabledMockSetInfo => {
|
||||
set({ enabledMockSetInfo });
|
||||
},
|
||||
addMockComp: mockSetInfo => {
|
||||
set(
|
||||
produce<MockInfoStoreState>(s => {
|
||||
const index = s.currentMockComp.findIndex(item =>
|
||||
isCurrent(item, mockSetInfo),
|
||||
);
|
||||
index <= -1 && s.currentMockComp.push(mockSetInfo);
|
||||
}),
|
||||
);
|
||||
return get().currentMockComp.length;
|
||||
},
|
||||
removeMockComp: mockSetInfo => {
|
||||
set(
|
||||
produce<MockInfoStoreState>(s => {
|
||||
const index = s.currentMockComp.findIndex(item =>
|
||||
isCurrent(item, mockSetInfo),
|
||||
);
|
||||
if (index > -1) {
|
||||
s.currentMockComp.splice(index, 1);
|
||||
}
|
||||
}),
|
||||
);
|
||||
return get().currentMockComp.length;
|
||||
},
|
||||
setTimer: timer => {
|
||||
set({ timer });
|
||||
},
|
||||
setRestartTimer: timer => {
|
||||
set({ timer });
|
||||
},
|
||||
}));
|
||||
@@ -0,0 +1,196 @@
|
||||
/*
|
||||
* Copyright 2025 coze-dev Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { useEffect, useRef } from 'react';
|
||||
|
||||
import { nanoid } from 'nanoid';
|
||||
import axios, { type Canceler } from 'axios';
|
||||
import { logger } from '@coze-arch/logger';
|
||||
import {
|
||||
type BizCtx,
|
||||
type MockSet,
|
||||
type MockSetBinding,
|
||||
} from '@coze-arch/bot-api/debugger_api';
|
||||
import { debuggerApi } from '@coze-arch/bot-api';
|
||||
|
||||
import { MockTrafficEnabled } from '../../util/get-mock-set-options';
|
||||
import { isSameScene } from '../../util';
|
||||
import { type EnabledMockSetInfo, useMockInfoStore } from './store';
|
||||
|
||||
function combineBindMockSetInfo(
|
||||
mockSetBindingList: Array<MockSetBinding>,
|
||||
mockSetDetailSet: Record<string, MockSet>,
|
||||
): Array<EnabledMockSetInfo> {
|
||||
return mockSetBindingList.map(mockSetInfo => {
|
||||
const { mockSetID } = mockSetInfo;
|
||||
const detail = mockSetID ? mockSetDetailSet[mockSetID] : {};
|
||||
return {
|
||||
mockSetBinding: mockSetInfo,
|
||||
mockSetDetail: detail,
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
// eslint-disable-next-line max-lines-per-function
|
||||
export const useInitialGetEnabledMockSet = ({
|
||||
bizCtx,
|
||||
pollingInterval,
|
||||
}: {
|
||||
bizCtx: BizCtx;
|
||||
pollingInterval?: number;
|
||||
}) => {
|
||||
const {
|
||||
enabledMockSetInfo,
|
||||
setPolling,
|
||||
setEnabledMockSetInfo,
|
||||
bizCtx: currentBizCtx,
|
||||
setCurrentBizCtx,
|
||||
addMockComp,
|
||||
removeMockComp,
|
||||
isPolling,
|
||||
setTimer,
|
||||
timer,
|
||||
currentMockComp,
|
||||
setLoading,
|
||||
isLoading,
|
||||
restartTimer,
|
||||
setRestartTimer,
|
||||
} = useMockInfoStore();
|
||||
const status = useRef<boolean>(false);
|
||||
const lastRequestId = useRef(0);
|
||||
const pollingTurnRef = useRef<string>();
|
||||
|
||||
const cancelReq = useRef<Canceler>();
|
||||
|
||||
const requestFn = async (curBizCtx: BizCtx) => {
|
||||
const currentRequestId = ++lastRequestId.current;
|
||||
const currentPollingTurn = pollingTurnRef.current;
|
||||
try {
|
||||
const { mockSetBindings = [], mockSetDetails = {} } =
|
||||
await debuggerApi.MGetMockSetBinding(
|
||||
{
|
||||
bizCtx: curBizCtx,
|
||||
needMockSetDetail: true,
|
||||
},
|
||||
{
|
||||
headers: {
|
||||
'rpc-persist-mock-traffic-enable': MockTrafficEnabled.ENABLE,
|
||||
},
|
||||
cancelToken: new axios.CancelToken(function executor(c) {
|
||||
cancelReq.current = c;
|
||||
}),
|
||||
},
|
||||
);
|
||||
|
||||
if (
|
||||
(currentRequestId > 1 && currentRequestId !== lastRequestId.current) ||
|
||||
!pollingTurnRef.current ||
|
||||
pollingTurnRef.current !== currentPollingTurn
|
||||
) {
|
||||
return;
|
||||
}
|
||||
setEnabledMockSetInfo?.(
|
||||
combineBindMockSetInfo(mockSetBindings, mockSetDetails),
|
||||
);
|
||||
return { mockSetBindings, mockSetDetails };
|
||||
} catch (e) {
|
||||
if (axios.isCancel(e)) {
|
||||
logger.info('poll_scene_mockset_canceled');
|
||||
} else {
|
||||
// @ts-expect-error -- linter-disable-autofix
|
||||
logger.error({ error: e, eventName: 'poll_scene_mockset_fail' });
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const request = async () => {
|
||||
try {
|
||||
const {
|
||||
trafficCallerID,
|
||||
connectorID,
|
||||
connectorUID,
|
||||
bizSpaceID,
|
||||
trafficScene,
|
||||
} = bizCtx;
|
||||
!status.current && (status.current = true);
|
||||
setLoading(true);
|
||||
if (status.current && pollingInterval) {
|
||||
setPolling(true);
|
||||
const id = setTimeout(() => {
|
||||
status.current && request();
|
||||
}, pollingInterval);
|
||||
setTimer(id);
|
||||
}
|
||||
await requestFn({
|
||||
trafficCallerID,
|
||||
connectorID,
|
||||
connectorUID,
|
||||
bizSpaceID,
|
||||
trafficScene,
|
||||
});
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
// 取消
|
||||
const cancel = () => {
|
||||
pollingTurnRef.current = undefined;
|
||||
cancelReq.current?.();
|
||||
lastRequestId.current = 0;
|
||||
cancelRestartTask();
|
||||
|
||||
if (timer) {
|
||||
clearTimeout(timer);
|
||||
setPolling(false);
|
||||
setTimer(undefined);
|
||||
status.current && (status.current = false);
|
||||
}
|
||||
};
|
||||
|
||||
const cancelRestartTask = () => {
|
||||
if (restartTimer) {
|
||||
clearTimeout(restartTimer);
|
||||
setRestartTimer(undefined);
|
||||
}
|
||||
};
|
||||
|
||||
const start = async () => {
|
||||
cancel();
|
||||
pollingTurnRef.current = nanoid();
|
||||
await request();
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
if (currentBizCtx && isSameScene(bizCtx, currentBizCtx)) {
|
||||
return;
|
||||
}
|
||||
setCurrentBizCtx(bizCtx);
|
||||
}, [bizCtx]);
|
||||
|
||||
return {
|
||||
start,
|
||||
cancel,
|
||||
isLoading,
|
||||
data: enabledMockSetInfo,
|
||||
addMockComp,
|
||||
removeMockComp,
|
||||
currentMockComp,
|
||||
isPolling,
|
||||
setRestartTimer,
|
||||
restartTimer,
|
||||
};
|
||||
};
|
||||
Reference in New Issue
Block a user