refactor(web): migrate workflow run history from useSWR to TanStack Query (#30077)

This commit is contained in:
yyh 2025-12-24 18:21:01 +08:00 committed by GitHub
parent 64a14dcdbc
commit 5896bc89f5
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 19 additions and 45 deletions

View File

@ -8,7 +8,6 @@ import Header from '@/app/components/workflow/header'
import { import {
useStore, useStore,
} from '@/app/components/workflow/store' } from '@/app/components/workflow/store'
import { fetchWorkflowRunHistory } from '@/service/workflow'
import InputFieldButton from './input-field-button' import InputFieldButton from './input-field-button'
import Publisher from './publisher' import Publisher from './publisher'
import RunMode from './run-mode' import RunMode from './run-mode'
@ -21,7 +20,6 @@ const RagPipelineHeader = () => {
const viewHistoryProps = useMemo(() => { const viewHistoryProps = useMemo(() => {
return { return {
historyUrl: `/rag/pipelines/${pipelineId}/workflow-runs`, historyUrl: `/rag/pipelines/${pipelineId}/workflow-runs`,
historyFetcher: fetchWorkflowRunHistory,
} }
}, [pipelineId]) }, [pipelineId])

View File

@ -58,16 +58,12 @@ vi.mock('@/app/components/app/store', () => ({
vi.mock('@/app/components/workflow/header', () => ({ vi.mock('@/app/components/workflow/header', () => ({
__esModule: true, __esModule: true,
default: (props: HeaderProps) => { default: (props: HeaderProps) => {
const historyFetcher = props.normal?.runAndHistoryProps?.viewHistoryProps?.historyFetcher
const hasHistoryFetcher = typeof historyFetcher === 'function'
return ( return (
<div <div
data-testid="workflow-header" data-testid="workflow-header"
data-show-run={String(Boolean(props.normal?.runAndHistoryProps?.showRunButton))} data-show-run={String(Boolean(props.normal?.runAndHistoryProps?.showRunButton))}
data-show-preview={String(Boolean(props.normal?.runAndHistoryProps?.showPreviewButton))} data-show-preview={String(Boolean(props.normal?.runAndHistoryProps?.showPreviewButton))}
data-history-url={props.normal?.runAndHistoryProps?.viewHistoryProps?.historyUrl ?? ''} data-history-url={props.normal?.runAndHistoryProps?.viewHistoryProps?.historyUrl ?? ''}
data-has-history-fetcher={String(hasHistoryFetcher)}
> >
<button <button
type="button" type="button"
@ -86,11 +82,6 @@ vi.mock('@/app/components/workflow/header', () => ({
}, },
})) }))
vi.mock('@/service/workflow', () => ({
__esModule: true,
fetchWorkflowRunHistory: vi.fn(),
}))
vi.mock('@/service/use-workflow', () => ({ vi.mock('@/service/use-workflow', () => ({
__esModule: true, __esModule: true,
useResetWorkflowVersionHistory: () => mockResetWorkflowVersionHistory, useResetWorkflowVersionHistory: () => mockResetWorkflowVersionHistory,
@ -127,7 +118,6 @@ describe('WorkflowHeader', () => {
expect(header).toHaveAttribute('data-show-run', 'false') expect(header).toHaveAttribute('data-show-run', 'false')
expect(header).toHaveAttribute('data-show-preview', 'true') expect(header).toHaveAttribute('data-show-preview', 'true')
expect(header).toHaveAttribute('data-history-url', '/apps/app-id/advanced-chat/workflow-runs') expect(header).toHaveAttribute('data-history-url', '/apps/app-id/advanced-chat/workflow-runs')
expect(header).toHaveAttribute('data-has-history-fetcher', 'true')
}) })
it('should configure run mode when app is not in advanced chat mode', () => { it('should configure run mode when app is not in advanced chat mode', () => {
@ -142,7 +132,6 @@ describe('WorkflowHeader', () => {
expect(header).toHaveAttribute('data-show-run', 'true') expect(header).toHaveAttribute('data-show-run', 'true')
expect(header).toHaveAttribute('data-show-preview', 'false') expect(header).toHaveAttribute('data-show-preview', 'false')
expect(header).toHaveAttribute('data-history-url', '/apps/app-id/workflow-runs') expect(header).toHaveAttribute('data-history-url', '/apps/app-id/workflow-runs')
expect(header).toHaveAttribute('data-has-history-fetcher', 'true')
}) })
}) })

View File

@ -8,9 +8,6 @@ import { useShallow } from 'zustand/react/shallow'
import { useStore as useAppStore } from '@/app/components/app/store' import { useStore as useAppStore } from '@/app/components/app/store'
import Header from '@/app/components/workflow/header' import Header from '@/app/components/workflow/header'
import { useResetWorkflowVersionHistory } from '@/service/use-workflow' import { useResetWorkflowVersionHistory } from '@/service/use-workflow'
import {
fetchWorkflowRunHistory,
} from '@/service/workflow'
import { useIsChatMode } from '../../hooks' import { useIsChatMode } from '../../hooks'
import ChatVariableTrigger from './chat-variable-trigger' import ChatVariableTrigger from './chat-variable-trigger'
import FeaturesTrigger from './features-trigger' import FeaturesTrigger from './features-trigger'
@ -33,7 +30,6 @@ const WorkflowHeader = () => {
return { return {
onClearLogAndMessageModal: handleClearLogAndMessageModal, onClearLogAndMessageModal: handleClearLogAndMessageModal,
historyUrl: isChatMode ? `/apps/${appDetail!.id}/advanced-chat/workflow-runs` : `/apps/${appDetail!.id}/workflow-runs`, historyUrl: isChatMode ? `/apps/${appDetail!.id}/advanced-chat/workflow-runs` : `/apps/${appDetail!.id}/workflow-runs`,
historyFetcher: fetchWorkflowRunHistory,
} }
}, [appDetail, isChatMode, handleClearLogAndMessageModal]) }, [appDetail, isChatMode, handleClearLogAndMessageModal])

View File

@ -1,17 +1,13 @@
import type { Fetcher } from 'swr'
import type { WorkflowRunHistoryResponse } from '@/types/workflow'
import { import {
RiCheckboxCircleLine, RiCheckboxCircleLine,
RiCloseLine, RiCloseLine,
RiErrorWarningLine, RiErrorWarningLine,
} from '@remixicon/react' } from '@remixicon/react'
import { noop } from 'lodash-es'
import { import {
memo, memo,
useState, useState,
} from 'react' } from 'react'
import { useTranslation } from 'react-i18next' import { useTranslation } from 'react-i18next'
import useSWR from 'swr'
import { AlertTriangle } from '@/app/components/base/icons/src/vender/line/alertsAndFeedback' import { AlertTriangle } from '@/app/components/base/icons/src/vender/line/alertsAndFeedback'
import { import {
ClockPlay, ClockPlay,
@ -30,6 +26,7 @@ import {
useWorkflowStore, useWorkflowStore,
} from '@/app/components/workflow/store' } from '@/app/components/workflow/store'
import { useFormatTimeFromNow } from '@/hooks/use-format-time-from-now' import { useFormatTimeFromNow } from '@/hooks/use-format-time-from-now'
import { useWorkflowRunHistory } from '@/service/use-workflow'
import { cn } from '@/utils/classnames' import { cn } from '@/utils/classnames'
import { import {
useIsChatMode, useIsChatMode,
@ -44,13 +41,11 @@ export type ViewHistoryProps = {
withText?: boolean withText?: boolean
onClearLogAndMessageModal?: () => void onClearLogAndMessageModal?: () => void
historyUrl?: string historyUrl?: string
historyFetcher?: Fetcher<WorkflowRunHistoryResponse, string>
} }
const ViewHistory = ({ const ViewHistory = ({
withText, withText,
onClearLogAndMessageModal, onClearLogAndMessageModal,
historyUrl, historyUrl,
historyFetcher,
}: ViewHistoryProps) => { }: ViewHistoryProps) => {
const { t } = useTranslation() const { t } = useTranslation()
const isChatMode = useIsChatMode() const isChatMode = useIsChatMode()
@ -68,11 +63,11 @@ const ViewHistory = ({
const { handleBackupDraft } = useWorkflowRun() const { handleBackupDraft } = useWorkflowRun()
const { closeAllInputFieldPanels } = useInputFieldPanel() const { closeAllInputFieldPanels } = useInputFieldPanel()
const fetcher = historyFetcher ?? (noop as Fetcher<WorkflowRunHistoryResponse, string>) const shouldFetchHistory = open && !!historyUrl
const { const {
data, data,
isLoading, isLoading,
} = useSWR((open && historyUrl && historyFetcher) ? historyUrl : null, fetcher) } = useWorkflowRunHistory(historyUrl, shouldFetchHistory)
return ( return (
( (

View File

@ -9,6 +9,7 @@ import type {
UpdateWorkflowParams, UpdateWorkflowParams,
VarInInspect, VarInInspect,
WorkflowConfigResponse, WorkflowConfigResponse,
WorkflowRunHistoryResponse,
} from '@/types/workflow' } from '@/types/workflow'
import { useInfiniteQuery, useMutation, useQuery, useQueryClient } from '@tanstack/react-query' import { useInfiniteQuery, useMutation, useQuery, useQueryClient } from '@tanstack/react-query'
import { del, get, patch, post, put } from './base' import { del, get, patch, post, put } from './base'
@ -25,6 +26,14 @@ export const useAppWorkflow = (appID: string) => {
}) })
} }
export const useWorkflowRunHistory = (url?: string, enabled = true) => {
return useQuery<WorkflowRunHistoryResponse>({
queryKey: [NAME_SPACE, 'runHistory', url],
queryFn: () => get<WorkflowRunHistoryResponse>(url as string),
enabled: !!url && enabled,
})
}
export const useInvalidateAppWorkflow = () => { export const useInvalidateAppWorkflow = () => {
const queryClient = useQueryClient() const queryClient = useQueryClient()
return (appID: string) => { return (appID: string) => {

View File

@ -1,14 +1,11 @@
import type { Fetcher } from 'swr'
import type { BlockEnum } from '@/app/components/workflow/types' import type { BlockEnum } from '@/app/components/workflow/types'
import type { CommonResponse } from '@/models/common' import type { CommonResponse } from '@/models/common'
import type { FlowType } from '@/types/common' import type { FlowType } from '@/types/common'
import type { import type {
ChatRunHistoryResponse,
ConversationVariableResponse, ConversationVariableResponse,
FetchWorkflowDraftResponse, FetchWorkflowDraftResponse,
NodesDefaultConfigsResponse, NodesDefaultConfigsResponse,
VarInInspect, VarInInspect,
WorkflowRunHistoryResponse,
} from '@/types/workflow' } from '@/types/workflow'
import { get, post } from './base' import { get, post } from './base'
import { getFlowPrefix } from './utils' import { getFlowPrefix } from './utils'
@ -24,18 +21,10 @@ export const syncWorkflowDraft = ({ url, params }: {
return post<CommonResponse & { updated_at: number, hash: string }>(url, { body: params }, { silent: true }) return post<CommonResponse & { updated_at: number, hash: string }>(url, { body: params }, { silent: true })
} }
export const fetchNodesDefaultConfigs: Fetcher<NodesDefaultConfigsResponse, string> = (url) => { export const fetchNodesDefaultConfigs = (url: string) => {
return get<NodesDefaultConfigsResponse>(url) return get<NodesDefaultConfigsResponse>(url)
} }
export const fetchWorkflowRunHistory: Fetcher<WorkflowRunHistoryResponse, string> = (url) => {
return get<WorkflowRunHistoryResponse>(url)
}
export const fetchChatRunHistory: Fetcher<ChatRunHistoryResponse, string> = (url) => {
return get<ChatRunHistoryResponse>(url)
}
export const singleNodeRun = (flowType: FlowType, flowId: string, nodeId: string, params: object) => { export const singleNodeRun = (flowType: FlowType, flowId: string, nodeId: string, params: object) => {
return post(`${getFlowPrefix(flowType)}/${flowId}/workflows/draft/nodes/${nodeId}/run`, { body: params }) return post(`${getFlowPrefix(flowType)}/${flowId}/workflows/draft/nodes/${nodeId}/run`, { body: params })
} }
@ -48,7 +37,7 @@ export const getLoopSingleNodeRunUrl = (flowType: FlowType, isChatFlow: boolean,
return `${getFlowPrefix(flowType)}/${flowId}/${isChatFlow ? 'advanced-chat/' : ''}workflows/draft/loop/nodes/${nodeId}/run` return `${getFlowPrefix(flowType)}/${flowId}/${isChatFlow ? 'advanced-chat/' : ''}workflows/draft/loop/nodes/${nodeId}/run`
} }
export const fetchPublishedWorkflow: Fetcher<FetchWorkflowDraftResponse, string> = (url) => { export const fetchPublishedWorkflow = (url: string) => {
return get<FetchWorkflowDraftResponse>(url) return get<FetchWorkflowDraftResponse>(url)
} }
@ -68,15 +57,13 @@ export const fetchPipelineNodeDefault = (pipelineId: string, blockType: BlockEnu
}) })
} }
// TODO: archived export const fetchCurrentValueOfConversationVariable = ({
export const updateWorkflowDraftFromDSL = (appId: string, data: string) => { url,
return post<FetchWorkflowDraftResponse>(`apps/${appId}/workflows/draft/import`, { body: { data } }) params,
} }: {
export const fetchCurrentValueOfConversationVariable: Fetcher<ConversationVariableResponse, {
url: string url: string
params: { conversation_id: string } params: { conversation_id: string }
}> = ({ url, params }) => { }) => {
return get<ConversationVariableResponse>(url, { params }) return get<ConversationVariableResponse>(url, { params })
} }