refactor(web): remove synthetic app detail SSO field (#42888)

This commit is contained in:
yyh authored and GitHub committed 2026-09-24 09:06:19 +00:00
1 parent 54c2928734
commit 7ee0f6af7e
24 files changed
+65 -85

No files matched your search

@@ -197,8 +197,7 @@ const AppDetailLayout: FC<IAppDetailLayoutProps> = (props) => {
return
}
if (appDetailRes && appDetail?.id !== appDetailRes.id)
setAppDetail({ ...appDetailRes, enable_sso: false })
if (appDetailRes && appDetail?.id !== appDetailRes.id) setAppDetail(appDetailRes)
}, [
appDetail?.id,
appDetailRes,
@@ -1,4 +1,4 @@
import type { App, AppSSO } from '@/types/app'
import type { App } from '@/types/app'
import { act, fireEvent, render, screen, waitFor } from '@testing-library/react'
import userEvent from '@testing-library/user-event'
import * as React from 'react'
@@ -107,7 +107,7 @@ vi.mock('@/app/components/app/export-confirm-modal', () => ({
),
}))
const createAppDetail = (overrides: Partial<App> = {}): App & Partial<AppSSO> =>
const createAppDetail = (overrides: Partial<App> = {}): App =>
({
id: 'app-1',
name: 'Test App',
@@ -120,7 +120,7 @@ const createAppDetail = (overrides: Partial<App> = {}): App & Partial<AppSSO> =>
use_icon_as_answer_icon: false,
max_active_requests: null,
...overrides,
}) as App & Partial<AppSSO>
}) as App
const defaultProps = {
appDetail: createAppDetail(),
@@ -1,4 +1,4 @@
import type { App, AppSSO } from '@/types/app'
import type { App } from '@/types/app'
import { screen } from '@testing-library/react'
import userEvent from '@testing-library/user-event'
import * as React from 'react'
@@ -54,7 +54,7 @@ const defaultAppPermissionKeys = [
AppACLPermission.Delete,
]
const createAppDetail = (overrides: Partial<App> = {}): App & Partial<AppSSO> =>
const createAppDetail = (overrides: Partial<App> = {}): App =>
({
id: 'app-1',
name: 'Test App',
@@ -68,7 +68,7 @@ const createAppDetail = (overrides: Partial<App> = {}): App & Partial<AppSSO> =>
permission_keys: defaultAppPermissionKeys,
maintainer: 'user-1',
...overrides,
}) as App & Partial<AppSSO>
}) as App
const createProps = (overrides: Partial<React.ComponentProps<typeof AppInfoTrigger>> = {}) => ({
appDetail: createAppDetail(),
@@ -2,7 +2,7 @@ import type { EnvironmentVariableItemResponse } from '@dify/contracts/api/consol
import type { AppInfoModalType } from './use-app-info-actions'
import type { DuplicateAppModalProps } from '@/app/components/app/duplicate-modal'
import type { CreateAppModalProps } from '@/app/components/explore/create-app-modal'
import type { App, AppSSO } from '@/types/app'
import type { App } from '@/types/app'
import {
AlertDialog,
AlertDialogActions,
@@ -35,7 +35,7 @@ const UpdateDSLModal = dynamic(() => import('@/app/components/workflow/update-ds
})
type AppInfoModalsProps = {
appDetail: App & Partial<AppSSO>
appDetail: App
activeModal: AppInfoModalType
closeModal: () => void
secretEnvList: EnvironmentVariableItemResponse[]
@@ -1,6 +1,6 @@
import type { Operation } from './app-operations'
import type { AppInfoModalType } from './use-app-info-actions'
import type { App, AppSSO } from '@/types/app'
import type { App } from '@/types/app'
import { cn } from '@langgenius/dify-ui/cn'
import { useSuspenseQuery } from '@tanstack/react-query'
import { useAtomValue } from 'jotai'
@@ -15,7 +15,7 @@ import { getAppModeLabel } from './app-mode-labels'
import AppOperations from './app-operations'
type AppInfoTriggerProps = {
appDetail: App & Partial<AppSSO>
appDetail: App
expand: boolean
openModal: (modal: Exclude<AppInfoModalType, null>) => void
isExporting: boolean
@@ -1,4 +1,5 @@
import type { AccessPointAppInfo, PublishedWorkflow } from '../shared/utils'
import type { PublishedWorkflow } from '../shared/utils'
import type { App } from '@/types/app'
import { QueryClientProvider } from '@tanstack/react-query'
import { screen, waitFor } from '@testing-library/react'
import userEvent from '@testing-library/user-event'
@@ -76,13 +77,13 @@ const appInfo = {
},
],
},
} as AccessPointAppInfo
} as App
const workflowAppInfo = {
...appInfo,
mode: AppModeEnum.WORKFLOW,
model_config: null,
} as unknown as AccessPointAppInfo
} as unknown as App
const publishedWorkflow = {
graph: {
@@ -108,7 +109,7 @@ function createDeferredPromise<T>() {
return { promise, reject, resolve }
}
function renderCard(cardAppInfo: AccessPointAppInfo = appInfo, workflow?: PublishedWorkflow) {
function renderCard(cardAppInfo: App = appInfo, workflow?: PublishedWorkflow) {
const queryClient = createTestQueryClient()
return render(
@@ -1,4 +1,4 @@
import type { AccessPointAppInfo } from '../shared/utils'
import type { App } from '@/types/app'
import { QueryClientProvider } from '@tanstack/react-query'
import { screen, waitFor } from '@testing-library/react'
import userEvent from '@testing-library/user-event'
@@ -53,24 +53,21 @@ vi.mock('../shared/api-secret-key-button', () => ({
},
}))
function createAppInfo(
mode: AppModeEnum,
overrides: Partial<AccessPointAppInfo> = {},
): AccessPointAppInfo {
function createAppInfo(mode: AppModeEnum, overrides: Partial<App> = {}): App {
return {
api_base_url: 'https://api.example.test/v1',
enable_api: true,
id: 'app-1',
mode,
...overrides,
} as AccessPointAppInfo
} as App
}
function renderCard(
mode: AppModeEnum,
availability: 'available' | 'loading' | 'unavailable' = 'available',
canManage = true,
overrides: Partial<AccessPointAppInfo> = {},
overrides: Partial<App> = {},
) {
useAppStore.setState({ appDetail: createAppInfo(mode, overrides) })
const queryClient = createTestQueryClient()
@@ -1,5 +1,5 @@
import type { AccessPointAppInfo } from '../shared/utils'
import type { AppTrigger } from '@/service/use-tools'
import type { App } from '@/types/app'
import { QueryClientProvider } from '@tanstack/react-query'
import { screen, waitFor } from '@testing-library/react'
import userEvent from '@testing-library/user-event'
@@ -72,7 +72,7 @@ vi.mock('@/app/components/workflow/block-icon', () => ({
const appInfo = {
id: 'app-1',
mode: AppModeEnum.WORKFLOW,
} as AccessPointAppInfo
} as App
function createTrigger(id: string, status: AppTrigger['status']): AppTrigger {
return {
@@ -1,5 +1,6 @@
import type { AccessPointAppInfo, PublishedWorkflow } from '../shared/utils'
import type { PublishedWorkflow } from '../shared/utils'
import type { InputVar, Node } from '@/app/components/workflow/types'
import type { App } from '@/types/app'
import { QueryClientProvider } from '@tanstack/react-query'
import { screen, waitFor, within } from '@testing-library/react'
import userEvent from '@testing-library/user-event'
@@ -109,7 +110,7 @@ vi.mock('@/app/components/app/overview/embedded', () => ({
) : null,
}))
function createAppInfo(mode: AppModeEnum): AccessPointAppInfo {
function createAppInfo(mode: AppModeEnum): App {
return {
access_mode: AccessMode.PUBLIC,
api_base_url: 'https://api.example.test/v1',
@@ -124,7 +125,7 @@ function createAppInfo(mode: AppModeEnum): AccessPointAppInfo {
access_token: 'site-code',
app_base_url: 'https://site.example.test',
},
} as AccessPointAppInfo
} as App
}
function renderCard(
@@ -1,6 +1,7 @@
'use client'
import type { AccessPointAppInfo, PublishedWorkflow } from '../shared/utils'
import type { PublishedWorkflow } from '../shared/utils'
import type { App } from '@/types/app'
import {
AlertDialog,
AlertDialogActions,
@@ -30,7 +31,7 @@ import { useAccessPointStatusLabel } from '../shared/use-access-point-status-lab
import { getPublishedWorkflowNodes, isAdvancedApp } from '../shared/utils'
type MCPAccessPointCardProps = {
appInfo: AccessPointAppInfo
appInfo: App
canManageAccessPoint: boolean
highlighted?: boolean
triggerModeDisabled: boolean
@@ -1,7 +1,7 @@
'use client'
import type { AccessPointAppInfo } from '../shared/utils'
import type { AccessPointAvailability } from '@/app/components/base/access-point/status'
import type { App } from '@/types/app'
import { useMutation } from '@tanstack/react-query'
import { useTranslation } from 'react-i18next'
import { useStore as useAppStore } from '@/app/components/app/store'
@@ -12,7 +12,7 @@ import { ServiceApiCardView } from '../shared/service-api-card-view'
import { getBuiltInAccessUrls } from '../shared/utils'
type ServiceApiAccessPointCardProps = {
appInfo: AccessPointAppInfo
appInfo: App
availability: AccessPointAvailability
canManage: boolean
highlighted?: boolean
@@ -1,8 +1,8 @@
'use client'
import type { AccessPointAppInfo } from '../shared/utils'
import type { TriggerWithProvider } from '@/app/components/workflow/block-selector/types'
import type { AppTrigger } from '@/service/use-tools'
import type { App } from '@/types/app'
import { StatusDot } from '@langgenius/dify-ui/status-dot'
import { Switch } from '@langgenius/dify-ui/switch'
import { useMutation } from '@tanstack/react-query'
@@ -131,7 +131,7 @@ function TriggerAccessPointItem({
}
type TriggerAccessPointCardProps = {
appInfo: AccessPointAppInfo
appInfo: App
availability: 'available' | 'loading' | 'unavailable'
canManageAccessPoint: boolean
highlighted?: boolean
@@ -1,9 +1,10 @@
'use client'
import type { SelectorParam } from 'i18next'
import type { AccessPointAppInfo, PublishedWorkflow } from '../shared/utils'
import type { PublishedWorkflow } from '../shared/utils'
import type { ConfigParams } from '@/app/components/app/overview/settings'
import type { AccessPointAvailability } from '@/app/components/base/access-point/status'
import type { App } from '@/types/app'
import {
AlertDialog,
AlertDialogActions,
@@ -57,7 +58,7 @@ const ACCESS_MODE_LABEL_MAP: Record<AccessMode, SelectorParam<'app'>> = {
}
type WebAppAccessPointCardProps = {
appInfo: AccessPointAppInfo
appInfo: App
availability: AccessPointAvailability
canDeploy: boolean
canManageAccessPoint: boolean
@@ -1,6 +1,5 @@
'use client'
import type { AccessPointAppInfo } from '../shared/utils'
import {
AlertDialog,
AlertDialogActions,
@@ -62,7 +61,7 @@ export function EnvironmentWebAppCard({
'navigation',
])
const queryClient = useQueryClient()
const appInfo = useAppStore((state) => state.appDetail) as AccessPointAppInfo | null
const appInfo = useAppStore((state) => state.appDetail)
const { data: systemFeatures } = useSuspenseQuery(systemFeaturesQueryOptions())
const actions = useAccessPointActions(appId, canManageAccessPoint)
const [showSettings, setShowSettings] = useState(false)
@@ -1,13 +1,11 @@
import type { WorkflowResponse } from '@dify/contracts/api/console/apps/types.gen'
import type { InputVar, Node } from '@/app/components/workflow/types'
import type { AppDetailResponse } from '@/models/app'
import type { AppSSO } from '@/types/app'
import type { App } from '@/types/app'
import type { DocPathWithoutLang } from '@/types/doc-paths'
import { BlockEnum, isTriggerNode } from '@/app/components/workflow/types'
import { AppModeEnum } from '@/types/app'
import { basePath } from '@/utils/var'
export type AccessPointAppInfo = AppDetailResponse & Partial<AppSSO>
export type PublishedWorkflow = WorkflowResponse | null | undefined
type AppRouteMode = Exclude<AppModeEnum, 'agent'>
@@ -28,10 +26,7 @@ export function getAppApiReferencePath(appMode: AppModeEnum) {
return APP_API_REFERENCE_PATHS[appMode]
}
export function getPublishedWorkflowState(
appInfo: AccessPointAppInfo,
workflow: PublishedWorkflow,
) {
export function getPublishedWorkflowState(appInfo: App, workflow: PublishedWorkflow) {
const isWorkflowApp = appInfo.mode === AppModeEnum.WORKFLOW
const nodes = getPublishedWorkflowNodes(workflow)
const hasStartNode = nodes.some((node) => node.data.type === BlockEnum.Start)
@@ -51,7 +46,7 @@ export function getPublishedWorkflowNodes(workflow: PublishedWorkflow) {
: EMPTY_WORKFLOW_NODES
}
export function getBuiltInAccessUrls(appInfo: AccessPointAppInfo) {
export function getBuiltInAccessUrls(appInfo: App) {
const appMode =
appInfo.mode === AppModeEnum.COMPLETION || appInfo.mode === AppModeEnum.WORKFLOW
? appInfo.mode
@@ -75,6 +70,6 @@ export function getHiddenStartInputs(workflow: PublishedWorkflow) {
)
}
export function isAdvancedApp(appInfo: AccessPointAppInfo) {
export function isAdvancedApp(appInfo: App) {
return appInfo.mode === AppModeEnum.WORKFLOW || appInfo.mode === AppModeEnum.ADVANCED_CHAT
}
@@ -1,5 +1,5 @@
import type { InputVar } from '@/app/components/workflow/types'
import type { App, AppSSO } from '@/types/app'
import type { App } from '@/types/app'
import { fireEvent, screen, waitFor } from '@testing-library/react'
import * as React from 'react'
import { toast } from '@/app/components/app/configuration/toast'
@@ -128,7 +128,7 @@ describe('ConfigModal logic', () => {
useStore.setState({
appDetail: {
mode: AppModeEnum.CHAT,
} as App & Partial<AppSSO>,
} as App,
})
})
@@ -1,5 +1,5 @@
import type { InputVar } from '@/app/components/workflow/types'
import type { App, AppSSO } from '@/types/app'
import type { App } from '@/types/app'
import { fireEvent, screen } from '@testing-library/react'
import userEvent from '@testing-library/user-event'
import * as React from 'react'
@@ -58,7 +58,7 @@ describe('ConfigModal', () => {
useStore.setState({
appDetail: {
mode: AppModeEnum.CHAT,
} as App & Partial<AppSSO>,
} as App,
})
})
@@ -1,6 +1,5 @@
import type { ReactElement, ReactNode } from 'react'
import type { AppDetailResponse } from '@/models/app'
import type { AppSSO } from '@/types/app'
import type { SettingsAppInfo } from '../index'
import { fireEvent, screen, waitFor } from '@testing-library/react'
import { NuqsTestingAdapter } from 'nuqs/adapters/testing'
import { consoleQuery } from '@/service/console'
@@ -75,6 +74,7 @@ vi.mock('@/context/i18n', async () => {
})
const mockAppInfo = {
id: 'test-app',
site: {
title: 'Test App',
icon_type: 'emoji',
@@ -93,10 +93,9 @@ const mockAppInfo = {
use_icon_as_answer_icon: true,
},
mode: AppModeEnum.ADVANCED_CHAT,
enable_sso: false,
} as unknown as AppDetailResponse & Partial<AppSSO>
} satisfies SettingsAppInfo
const renderSettingsModal = (appInfo = mockAppInfo, canDeploy = false) =>
const renderSettingsModal = (appInfo: SettingsAppInfo = mockAppInfo, canDeploy = false) =>
render(
<SettingsModal
isChat
@@ -235,7 +234,6 @@ describe('SettingsModal', () => {
icon_background: mockAppInfo.site.icon_background,
show_workflow_steps: mockAppInfo.site.show_workflow_steps,
use_icon_as_answer_icon: mockAppInfo.site.use_icon_as_answer_icon,
enable_sso: mockAppInfo.enable_sso,
}),
)
expect(mockOnClose).toHaveBeenCalled()
@@ -322,15 +320,13 @@ describe('SettingsModal', () => {
isChat
canDeploy={false}
isShow={true}
appInfo={
{
...mockAppInfo,
site: {
...mockAppInfo.site,
input_placeholder: 'Updated prompt',
},
} as typeof mockAppInfo
}
appInfo={{
...mockAppInfo,
site: {
...mockAppInfo.site,
input_placeholder: 'Updated prompt',
},
}}
onClose={mockOnClose}
onSave={mockOnSave}
/>,
@@ -423,7 +419,7 @@ describe('SettingsModal', () => {
icon_background: null,
icon_url: 'https://example.com/uploaded.png',
},
} as typeof mockAppInfo
} satisfies SettingsAppInfo
renderSettingsModal(imageAppInfo)
@@ -89,7 +89,6 @@ type SettingsAppIconSelection =
export type SettingsAppInfo = {
id: string
mode: AppModeEnum
enable_sso?: boolean
site: SettingsSiteInfo
}
@@ -109,7 +108,6 @@ export type ConfigParams = {
icon_background?: string
show_workflow_steps: boolean
use_icon_as_answer_icon: boolean
enable_sso?: boolean
}
const INPUT_PLACEHOLDER_MAX_LENGTH = 64
@@ -153,7 +151,6 @@ const createInputInfo = (appInfo: ISettingsModalProps['appInfo']) => {
inputPlaceholder: input_placeholder ?? '',
show_workflow_steps,
use_icon_as_answer_icon,
enable_sso: appInfo.enable_sso,
}
}
@@ -170,7 +167,6 @@ const createAppIcon = (appInfo: ISettingsModalProps['appInfo']): SettingsAppIcon
const getSettingsResetKey = (appInfo: ISettingsModalProps['appInfo']) =>
JSON.stringify([
appInfo.id,
appInfo.enable_sso,
appInfo.site.title,
appInfo.site.description,
appInfo.site.chat_color_theme,
@@ -348,7 +344,6 @@ const SettingsModal: FC<ISettingsModalProps> = ({
icon_background: appIcon.type === 'emoji' ? appIcon.background : undefined,
show_workflow_steps: inputInfo.show_workflow_steps,
use_icon_as_answer_icon: inputInfo.use_icon_as_answer_icon,
enable_sso: inputInfo.enable_sso,
}
await onSave?.(params)
setSaveLoading(false)
+3 -3
View File
@@ -1,9 +1,9 @@
import type { IChatItem } from '@/app/components/base/chat/chat/type'
import type { App, AppSSO } from '@/types/app'
import type { App } from '@/types/app'
import { create } from 'zustand'
type State = {
appDetail?: App & Partial<AppSSO>
appDetail?: App
currentLogItem?: IChatItem
currentLogModalActiveTab: string
showPromptLogModal: boolean
@@ -13,7 +13,7 @@ type State = {
}
type Action = {
setAppDetail: (appDetail?: App & Partial<AppSSO>) => void
setAppDetail: (appDetail?: App) => void
setCurrentLogItem: (item?: IChatItem) => void
setCurrentLogModalActiveTab: (tab: string) => void
setShowPromptLogModal: (showPromptLogModal: boolean) => void
@@ -6,7 +6,7 @@ import type {
SelectFormInput,
} from '../../../types'
import type { CodeNodeType } from '@/app/components/workflow/nodes/code/types'
import type { App, AppSSO } from '@/types/app'
import type { App } from '@/types/app'
import { QueryClient, QueryClientProvider } from '@tanstack/react-query'
import { fireEvent, screen, waitFor } from '@testing-library/react'
import userEvent from '@testing-library/user-event'
@@ -184,7 +184,7 @@ describe('human-input/delivery-method/test-email-sender', () => {
appDetail: {
id: 'app-1',
name: 'Workflow App',
} as App & Partial<AppSSO>,
} as App,
})
})
@@ -1,5 +1,5 @@
import type { HistoryWorkflowData } from '@/app/components/workflow/types'
import type { App, AppSSO } from '@/types/app'
import type { App } from '@/types/app'
import { screen, waitFor } from '@testing-library/react'
import userEvent from '@testing-library/user-event'
import { useStore as useAppStore } from '@/app/components/app/store'
@@ -24,7 +24,7 @@ describe('ChatRecord', () => {
beforeEach(() => {
vi.clearAllMocks()
useAppStore.setState({
appDetail: { id: 'app-1' } as App & Partial<AppSSO>,
appDetail: { id: 'app-1' } as App,
})
})
@@ -187,8 +187,7 @@ export function WebAppAccessCard({
async function handleSaveSettings(params: ConfigParams) {
if (!appId || !canManageWebApp) return
const { enable_sso: _enableSso, ...body } = params
const sitePayload = body satisfies AppSiteUpdatePayload
const sitePayload = params satisfies AppSiteUpdatePayload
try {
await updateSiteMutation.mutateAsync({
-4
View File
@@ -445,10 +445,6 @@ export type App = {
permission_keys?: string[]
}
export type AppSSO = {
enable_sso: boolean
}
export type Resolution = 'low' | 'high'
export const Resolution = {
low: 'low' as Resolution,