mirror of
https://github.com/langgenius/dify.git
synced 2026-05-07 02:46:32 +08:00
feat(web): app switch api
This commit is contained in:
parent
3ac4caf735
commit
61e257b2a8
@ -1,3 +1,4 @@
|
||||
import type { WorkflowTypeConversionTarget } from '@/types/workflow'
|
||||
import { type } from '@orpc/contract'
|
||||
import { base } from '../base'
|
||||
|
||||
@ -12,3 +13,18 @@ export const appDeleteContract = base
|
||||
}
|
||||
}>())
|
||||
.output(type<unknown>())
|
||||
|
||||
export const appWorkflowTypeConvertContract = base
|
||||
.route({
|
||||
path: '/apps/{appId}/workflows/convert-type',
|
||||
method: 'POST',
|
||||
})
|
||||
.input(type<{
|
||||
params: {
|
||||
appId: string
|
||||
}
|
||||
query: {
|
||||
target_type: WorkflowTypeConversionTarget
|
||||
}
|
||||
}>())
|
||||
.output(type<unknown>())
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
import type { InferContractRouterInputs } from '@orpc/contract'
|
||||
import { appDeleteContract } from './console/apps'
|
||||
import { appDeleteContract, appWorkflowTypeConvertContract } from './console/apps'
|
||||
import { bindPartnerStackContract, invoicesContract } from './console/billing'
|
||||
import {
|
||||
availableEvaluationMetricsContract,
|
||||
@ -98,6 +98,7 @@ export const consoleRouterContract = {
|
||||
systemFeatures: systemFeaturesContract,
|
||||
apps: {
|
||||
deleteApp: appDeleteContract,
|
||||
convertWorkflowType: appWorkflowTypeConvertContract,
|
||||
},
|
||||
explore: {
|
||||
apps: exploreAppsContract,
|
||||
|
||||
96
web/service/__tests__/use-apps.spec.tsx
Normal file
96
web/service/__tests__/use-apps.spec.tsx
Normal file
@ -0,0 +1,96 @@
|
||||
import type { ReactNode } from 'react'
|
||||
import { QueryClient, QueryClientProvider } from '@tanstack/react-query'
|
||||
import { act, renderHook, waitFor } from '@testing-library/react'
|
||||
import { useConvertWorkflowTypeMutation } from '../use-apps'
|
||||
|
||||
const {
|
||||
invalidateQueries,
|
||||
convertWorkflowTypeMutationFn,
|
||||
convertWorkflowTypeMutationOptions,
|
||||
} = vi.hoisted(() => ({
|
||||
invalidateQueries: vi.fn(),
|
||||
convertWorkflowTypeMutationFn: vi.fn(),
|
||||
convertWorkflowTypeMutationOptions: vi.fn(),
|
||||
}))
|
||||
|
||||
vi.mock('@tanstack/react-query', async (importOriginal) => {
|
||||
const actual = await importOriginal<typeof import('@tanstack/react-query')>()
|
||||
|
||||
return {
|
||||
...actual,
|
||||
useQueryClient: () => ({
|
||||
invalidateQueries,
|
||||
}),
|
||||
}
|
||||
})
|
||||
|
||||
vi.mock('@/service/client', () => ({
|
||||
consoleClient: {},
|
||||
consoleQuery: {
|
||||
apps: {
|
||||
convertWorkflowType: {
|
||||
mutationOptions: convertWorkflowTypeMutationOptions,
|
||||
},
|
||||
},
|
||||
},
|
||||
}))
|
||||
|
||||
const createWrapper = () => {
|
||||
const queryClient = new QueryClient({
|
||||
defaultOptions: {
|
||||
queries: {
|
||||
retry: false,
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
return ({ children }: { children: ReactNode }) => (
|
||||
<QueryClientProvider client={queryClient}>{children}</QueryClientProvider>
|
||||
)
|
||||
}
|
||||
|
||||
// Scenario: workflow type conversion forwards the expected API input and refreshes app caches.
|
||||
describe('useConvertWorkflowTypeMutation', () => {
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks()
|
||||
convertWorkflowTypeMutationFn.mockResolvedValue({})
|
||||
convertWorkflowTypeMutationOptions.mockImplementation(options => ({
|
||||
mutationFn: convertWorkflowTypeMutationFn,
|
||||
onSuccess: options?.onSuccess,
|
||||
}))
|
||||
})
|
||||
|
||||
it('should convert workflow type and invalidate app queries when mutation succeeds', async () => {
|
||||
// Arrange
|
||||
const { result } = renderHook(() => useConvertWorkflowTypeMutation(), {
|
||||
wrapper: createWrapper(),
|
||||
})
|
||||
|
||||
// Act
|
||||
await act(async () => {
|
||||
result.current.mutate({
|
||||
params: { appId: 'app-1' },
|
||||
query: { target_type: 'evaluation' },
|
||||
})
|
||||
})
|
||||
|
||||
// Assert
|
||||
await waitFor(() => {
|
||||
expect(convertWorkflowTypeMutationFn).toHaveBeenCalledWith({
|
||||
params: { appId: 'app-1' },
|
||||
query: { target_type: 'evaluation' },
|
||||
})
|
||||
})
|
||||
await waitFor(() => {
|
||||
expect(invalidateQueries).toHaveBeenCalledWith({
|
||||
queryKey: ['apps', 'detail', 'app-1'],
|
||||
})
|
||||
})
|
||||
expect(invalidateQueries).toHaveBeenCalledWith({
|
||||
queryKey: ['apps', 'list'],
|
||||
})
|
||||
expect(invalidateQueries).toHaveBeenCalledWith({
|
||||
queryKey: ['apps', 'full-list'],
|
||||
})
|
||||
})
|
||||
})
|
||||
@ -11,6 +11,7 @@ import type {
|
||||
WorkflowDailyConversationsResponse,
|
||||
} from '@/models/app'
|
||||
import type { App } from '@/types/app'
|
||||
import type { WorkflowTypeConversionTarget } from '@/types/workflow'
|
||||
import {
|
||||
keepPreviousData,
|
||||
useInfiniteQuery,
|
||||
@ -148,6 +149,30 @@ export const useDeleteAppMutation = () => {
|
||||
})
|
||||
}
|
||||
|
||||
export const useConvertWorkflowTypeMutation = () => {
|
||||
const queryClient = useQueryClient()
|
||||
|
||||
return useMutation({
|
||||
...consoleQuery.apps.convertWorkflowType.mutationOptions({
|
||||
onSuccess: async (_, variables) => {
|
||||
await Promise.all([
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: [NAME_SPACE, 'detail', variables.params.appId],
|
||||
}),
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: [NAME_SPACE, 'list'],
|
||||
}),
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: useAppFullListKey,
|
||||
}),
|
||||
])
|
||||
},
|
||||
}),
|
||||
})
|
||||
}
|
||||
|
||||
export type { WorkflowTypeConversionTarget }
|
||||
|
||||
const useAppStatisticsQuery = <T>(metric: string, appId: string, params?: DateRangeParams) => {
|
||||
return useQuery<T>({
|
||||
queryKey: [NAME_SPACE, 'statistics', metric, appId, params],
|
||||
|
||||
@ -47,6 +47,14 @@ export enum AppModeEnum {
|
||||
}
|
||||
export const AppModes = [AppModeEnum.COMPLETION, AppModeEnum.WORKFLOW, AppModeEnum.CHAT, AppModeEnum.ADVANCED_CHAT, AppModeEnum.AGENT_CHAT] as const
|
||||
|
||||
export enum AppTypeEnum {
|
||||
WORKFLOW = 'workflow',
|
||||
CHAT = 'chat',
|
||||
RAG_PIPELINE = 'rag-pipeline',
|
||||
SNIPPET = 'snippet',
|
||||
EVALUATION = 'evaluation',
|
||||
}
|
||||
|
||||
/**
|
||||
* Variable type
|
||||
*/
|
||||
@ -325,6 +333,8 @@ export type App = {
|
||||
|
||||
/** Mode */
|
||||
mode: AppModeEnum
|
||||
/** Type */
|
||||
type?: AppTypeEnum
|
||||
/** Enable web app */
|
||||
enable_site: boolean
|
||||
/** Enable web API */
|
||||
|
||||
@ -427,6 +427,8 @@ export type PublishWorkflowParams = {
|
||||
releaseNotes: string
|
||||
}
|
||||
|
||||
export type WorkflowTypeConversionTarget = 'workflow' | 'evaluation'
|
||||
|
||||
export type UpdateWorkflowParams = {
|
||||
url: string
|
||||
title: string
|
||||
|
||||
Loading…
Reference in New Issue
Block a user