refactor(web): read app profile from query cache (#40467)

This commit is contained in:
yyh
2026-08-11 08:09:58 +00:00
committed by GitHub
parent c1e94f8ee7
commit 3d02a12eb6
46 changed files with 289 additions and 195 deletions
@@ -35,10 +35,6 @@ vi.mock('@/service/apps', () => ({
fetchAppDetailDirect: vi.fn(),
}))
vi.mock('@/context/account-state', async () => {
const { createAccountStateModuleMock } = await import('@/test/console/state-fixture')
return createAccountStateModuleMock(() => mockConsoleState)
})
vi.mock('@/context/workspace-state', async () => {
const { createWorkspaceStateModuleMock } = await import('@/test/console/state-fixture')
return createWorkspaceStateModuleMock(() => mockConsoleState)
@@ -10,12 +10,12 @@ import { useTranslation } from 'react-i18next'
import { useShallow } from 'zustand/react/shallow'
import { useStore } from '@/app/components/app/store'
import Loading from '@/app/components/base/loading'
import { userProfileIdAtom } from '@/context/account-state'
import {
workspacePermissionKeysAtom,
workspacePermissionKeysLoadingAtom,
} from '@/context/permission-state'
import { currentWorkspaceAtom, currentWorkspaceLoadingAtom } from '@/context/workspace-state'
import { userProfileQueryOptions } from '@/features/account-profile/client'
import { systemFeaturesQueryOptions } from '@/features/system-features/client'
import useDocumentTitle from '@/hooks/use-document-title'
import { usePathname, useRouter } from '@/next/navigation'
@@ -44,7 +44,10 @@ const AppDetailLayout: FC<IAppDetailLayoutProps> = (props) => {
const isLoadingCurrentWorkspace = useAtomValue(currentWorkspaceLoadingAtom)
const isLoadingWorkspacePermissionKeys = useAtomValue(workspacePermissionKeysLoadingAtom)
const currentWorkspace = useAtomValue(currentWorkspaceAtom)
const currentUserId = useAtomValue(userProfileIdAtom)
const { data: currentUserId } = useSuspenseQuery({
...userProfileQueryOptions(),
select: (data) => data.profile.id,
})
const workspacePermissionKeys = useAtomValue(workspacePermissionKeysAtom)
const isRbacEnabled = systemFeatures.rbac_enabled
const { appDetail, setAppDetail } = useStore(
@@ -1,7 +1,8 @@
import type { App } from '@/types/app'
import { screen, waitFor } from '@testing-library/react'
import userEvent from '@testing-library/user-event'
import { render } from '@/test/console/render'
import { createAccountProfileQueryWrapper } from '@/test/console/account-profile'
import { render as renderWithConsoleState } from '@/test/console/render'
import CardView from '../card-view'
const mockAppState = vi.hoisted(() => ({
@@ -19,6 +20,11 @@ const mockUpdateAppSiteAccessToken = vi.hoisted(() => vi.fn())
const mockFetchAppDetail = vi.hoisted(() => vi.fn())
const mockInvalidateQueries = vi.hoisted(() => vi.fn())
const render = (ui: Parameters<typeof renderWithConsoleState>[0]) =>
renderWithConsoleState(ui, {
wrapper: createAccountProfileQueryWrapper({ id: 'user-1' }),
})
vi.mock('@tanstack/react-query', async (importOriginal) => {
const actual = await importOriginal<typeof import('@tanstack/react-query')>()
return {
@@ -27,15 +33,27 @@ vi.mock('@tanstack/react-query', async (importOriginal) => {
}
})
vi.mock('@/service/client', () => ({
consoleQuery: {
apps: {
get: { key: () => ['console', 'apps', 'get'] },
starred: { get: { key: () => ['console', 'apps', 'starred', 'get'] } },
recent: { get: { key: () => ['console', 'apps', 'recent', 'get'] } },
vi.mock('@/service/client', async (importOriginal) => {
const actual = await importOriginal<typeof import('@/service/client')>()
return {
...actual,
consoleQuery: {
...actual.consoleQuery,
account: {
profile: {
get: {
queryKey: () => [['console', 'account', 'profile', 'get'], { type: 'query' }],
},
},
},
apps: {
get: { key: () => ['console', 'apps', 'get'] },
starred: { get: { key: () => ['console', 'apps', 'starred', 'get'] } },
recent: { get: { key: () => ['console', 'apps', 'recent', 'get'] } },
},
},
},
}))
}
})
vi.mock('@/app/components/app/store', () => ({
useStore: <T,>(selector: (state: typeof mockAppState) => T): T => selector(mockAppState),
@@ -52,14 +70,6 @@ vi.mock('@/service/apps', () => ({
updateAppSiteAccessToken: (...args: unknown[]) => mockUpdateAppSiteAccessToken(...args),
}))
vi.mock('@/context/account-state', async () => {
const { createAccountStateModuleMock } = await import('@/test/console/state-fixture')
return createAccountStateModuleMock(() => ({
userProfile: { id: 'user-1' },
currentWorkspace: { id: 'workspace-1' },
workspacePermissionKeys: mockAppState.appDetail.permission_keys,
}))
})
vi.mock('@/context/workspace-state', async () => {
const { createWorkspaceStateModuleMock } = await import('@/test/console/state-fixture')
return createWorkspaceStateModuleMock(() => ({
@@ -6,7 +6,7 @@ import type { UpdateAppSiteCodeResponse } from '@/models/app'
import type { App } from '@/types/app'
import type { I18nKeysByPrefix } from '@/types/i18n'
import { toast } from '@langgenius/dify-ui/toast'
import { useQueryClient } from '@tanstack/react-query'
import { useQueryClient, useSuspenseQuery } from '@tanstack/react-query'
import { useAtomValue } from 'jotai'
import { useCallback, useEffect, useMemo } from 'react'
import { useTranslation } from 'react-i18next'
@@ -18,8 +18,8 @@ import MCPServiceCard from '@/app/components/tools/mcp/mcp-service-card'
import { collaborationManager } from '@/app/components/workflow/collaboration/core/collaboration-manager'
import { webSocketClient } from '@/app/components/workflow/collaboration/core/websocket-manager'
import { isTriggerNode } from '@/app/components/workflow/types'
import { userProfileIdAtom } from '@/context/account-state'
import { workspacePermissionKeysAtom } from '@/context/permission-state'
import { userProfileQueryOptions } from '@/features/account-profile/client'
import {
fetchAppDetail,
updateAppSiteAccessToken,
@@ -43,7 +43,10 @@ const CardView: FC<ICardViewProps> = ({ appId, isInPanel, className }) => {
const queryClient = useQueryClient()
const appDetail = useAppStore((state) => state.appDetail)
const setAppDetail = useAppStore((state) => state.setAppDetail)
const currentUserId = useAtomValue(userProfileIdAtom)
const { data: currentUserId } = useSuspenseQuery({
...userProfileQueryOptions(),
select: (data) => data.profile.id,
})
const workspacePermissionKeys = useAtomValue(workspacePermissionKeysAtom)
const canEditApp = useMemo(
() =>
@@ -25,9 +25,9 @@ import {
WorkflowMessagesChart,
} from '@/app/components/app/overview/app-chart'
import { useStore as useAppStore } from '@/app/components/app/store'
import { userProfileIdAtom } from '@/context/account-state'
import { useDocLink } from '@/context/i18n'
import { workspacePermissionKeysAtom } from '@/context/permission-state'
import { userProfileQueryOptions } from '@/features/account-profile/client'
import { systemFeaturesQueryOptions } from '@/features/system-features/client'
import { getAppACLCapabilities } from '@/utils/permission'
import LongTimeRangePicker from './long-time-range-picker'
@@ -77,7 +77,10 @@ function ChartViewContent({
const isNonCloudEdition = deploymentEdition === 'COMMUNITY' || deploymentEdition === 'ENTERPRISE'
const docLink = useDocLink()
const appDetail = useAppStore((state) => state.appDetail)
const currentUserId = useAtomValue(userProfileIdAtom)
const { data: currentUserId } = useSuspenseQuery({
...userProfileQueryOptions(),
select: (data) => data.profile.id,
})
const workspacePermissionKeys = useAtomValue(workspacePermissionKeysAtom)
const canMonitor = React.useMemo(
() =>
@@ -1,4 +1,5 @@
'use client'
import type { FC } from 'react'
import type {
AliyunConfig,
@@ -16,6 +17,7 @@ import type { TracingStatus } from '@/models/app'
import { cn } from '@langgenius/dify-ui/cn'
import { StatusDot } from '@langgenius/dify-ui/status-dot'
import { toast } from '@langgenius/dify-ui/toast'
import { useSuspenseQuery } from '@tanstack/react-query'
import { useBoolean } from 'ahooks'
import { useAtomValue } from 'jotai'
import * as React from 'react'
@@ -36,8 +38,8 @@ import {
WeaveIcon,
} from '@/app/components/base/icons/src/public/tracing'
import Loading from '@/app/components/base/loading'
import { userProfileIdAtom } from '@/context/account-state'
import { workspacePermissionKeysAtom } from '@/context/permission-state'
import { userProfileQueryOptions } from '@/features/account-profile/client'
import { usePathname } from '@/next/navigation'
import {
fetchTracingConfig as doFetchTracingConfig,
@@ -56,7 +58,10 @@ const Panel: FC = () => {
const pathname = usePathname()
const matched = /\/app\/([^/]+)/.exec(pathname)
const appId = matched?.length && matched[1] ? matched[1] : ''
const currentUserId = useAtomValue(userProfileIdAtom)
const { data: currentUserId } = useSuspenseQuery({
...userProfileQueryOptions(),
select: (data) => data.profile.id,
})
const workspacePermissionKeys = useAtomValue(workspacePermissionKeysAtom)
const appDetail = useAppStore((s) => s.appDetail)
const appACLCapabilities = React.useMemo(
@@ -1,11 +1,12 @@
'use client'
import { useSuspenseQuery } from '@tanstack/react-query'
import { useAtomValue } from 'jotai'
import * as React from 'react'
import ApikeyInfoPanel from '@/app/components/app/overview/apikey-info-panel'
import { useStore as useAppStore } from '@/app/components/app/store'
import { userProfileIdAtom } from '@/context/account-state'
import { workspacePermissionKeysAtom } from '@/context/permission-state'
import { userProfileQueryOptions } from '@/features/account-profile/client'
import { getAppACLCapabilities } from '@/utils/permission'
import ChartView from './chart-view'
import TracingPanel from './tracing/panel'
@@ -16,7 +17,10 @@ type OverviewViewProps = {
const OverviewView = ({ appId }: OverviewViewProps) => {
const appDetail = useAppStore((state) => state.appDetail)
const currentUserId = useAtomValue(userProfileIdAtom)
const { data: currentUserId } = useSuspenseQuery({
...userProfileQueryOptions(),
select: (data) => data.profile.id,
})
const workspacePermissionKeys = useAtomValue(workspacePermissionKeysAtom)
const appACLCapabilities = React.useMemo(
() =>
@@ -22,8 +22,8 @@ import { useTranslation } from 'react-i18next'
import { useStore } from '@/app/components/app/store'
import Divider from '@/app/components/base/divider'
import Annotations from '@/app/components/base/icons/src/vender/Annotations'
import { userProfileIdAtom } from '@/context/account-state'
import { workspacePermissionKeysAtom } from '@/context/permission-state'
import { userProfileQueryOptions } from '@/features/account-profile/client'
import { systemFeaturesQueryOptions } from '@/features/system-features/client'
import { usePathname } from '@/next/navigation'
import { AppModeEnum } from '@/types/app'
@@ -71,7 +71,10 @@ const AppDetailSection = ({ expand = true }: AppDetailSectionProps) => {
const { t } = useTranslation()
const pathname = usePathname()
const { data: systemFeatures } = useSuspenseQuery(systemFeaturesQueryOptions())
const currentUserId = useAtomValue(userProfileIdAtom)
const { data: currentUserId } = useSuspenseQuery({
...userProfileQueryOptions(),
select: (data) => data.profile.id,
})
const workspacePermissionKeys = useAtomValue(workspacePermissionKeysAtom)
const isRbacEnabled = systemFeatures.rbac_enabled
const appDetail = useStore((state) => state.appDetail)
@@ -2,7 +2,8 @@ import type { App, AppSSO } from '@/types/app'
import { screen } from '@testing-library/react'
import userEvent from '@testing-library/user-event'
import * as React from 'react'
import { render } from '@/test/console/render'
import { createConsoleQueryWrapper } from '@/test/console/query-data'
import { render as renderWithConsoleState } from '@/test/console/render'
import { AppModeEnum } from '@/types/app'
import { AppACLPermission } from '@/utils/permission'
import AppInfoDetailPanel from '../app-info-detail-panel'
@@ -19,10 +20,11 @@ const mockConsoleState = vi.hoisted(() => ({
},
}))
vi.mock('@/context/account-state', async () => {
const { createAccountStateModuleMock } = await import('@/test/console/state-fixture')
return createAccountStateModuleMock(() => mockConsoleState.current)
})
const render = (ui: Parameters<typeof renderWithConsoleState>[0]) =>
renderWithConsoleState(ui, {
wrapper: createConsoleQueryWrapper({ accountProfile: { id: 'user-1' } }).wrapper,
})
vi.mock('@/context/permission-state', async () => {
const { createPermissionStateModuleMock } = await import('@/test/console/state-fixture')
return createPermissionStateModuleMock(() => mockConsoleState.current)
@@ -10,13 +10,14 @@ import {
RiFileDownloadLine,
RiFileUploadLine,
} from '@remixicon/react'
import { useSuspenseQuery } from '@tanstack/react-query'
import { useAtomValue } from 'jotai'
import * as React from 'react'
import { useMemo } from 'react'
import { useTranslation } from 'react-i18next'
import CardView from '@/app/(commonLayout)/app/(appDetailLayout)/[appId]/overview/card-view'
import { userProfileIdAtom } from '@/context/account-state'
import { workspacePermissionKeysAtom } from '@/context/permission-state'
import { userProfileQueryOptions } from '@/features/account-profile/client'
import { AppModeEnum } from '@/types/app'
import { getAppACLCapabilities, hasPermission } from '@/utils/permission'
import AppIcon from '../../base/app-icon'
@@ -42,7 +43,10 @@ const AppInfoDetailPanel = ({
exportCheck,
}: AppInfoDetailPanelProps) => {
const { t } = useTranslation()
const currentUserId = useAtomValue(userProfileIdAtom)
const { data: currentUserId } = useSuspenseQuery({
...userProfileQueryOptions(),
select: (data) => data.profile.id,
})
const workspacePermissionKeys = useAtomValue(workspacePermissionKeysAtom)
const appACLCapabilities = useMemo(
() =>
@@ -1,8 +1,9 @@
import type { AppInfoActions } from './use-app-info-actions'
import { useSuspenseQuery } from '@tanstack/react-query'
import { useAtomValue } from 'jotai'
import * as React from 'react'
import { userProfileIdAtom } from '@/context/account-state'
import { workspacePermissionKeysAtom } from '@/context/permission-state'
import { userProfileQueryOptions } from '@/features/account-profile/client'
import { getAppACLCapabilities } from '@/utils/permission'
import AppInfoDetailPanel from './app-info-detail-panel'
import AppInfoModals from './app-info-modals'
@@ -81,7 +82,10 @@ export const AppInfoView = ({
renderDetail = true,
}: AppInfoViewProps) => {
const { appDetail, panelOpen, setPanelOpen, activeModal, secretEnvList } = actions
const currentUserId = useAtomValue(userProfileIdAtom)
const { data: currentUserId } = useSuspenseQuery({
...userProfileQueryOptions(),
select: (data) => data.profile.id,
})
const workspacePermissionKeys = useAtomValue(workspacePermissionKeysAtom)
const appACLCapabilities = getAppACLCapabilities(appDetail?.permission_keys, {
currentUserId,
@@ -18,6 +18,7 @@ let mockIsRbacEnabled = true
const render = (ui: Parameters<typeof renderWithConsoleQuery>[0]) =>
renderWithConsoleQuery(ui, {
accountProfile: mockConsoleState.userProfile,
systemFeatures: {
rbac_enabled: mockIsRbacEnabled,
},
@@ -44,10 +45,6 @@ const mockMutations = vi.hoisted(() => ({
removeMemberBindings: vi.fn(),
}))
vi.mock('@/context/account-state', async () => {
const { createAccountStateModuleMock } = await import('@/test/console/state-fixture')
return createAccountStateModuleMock(() => mockConsoleState)
})
vi.mock('@/context/permission-state', async () => {
const { createPermissionStateModuleMock } = await import('@/test/console/state-fixture')
return createPermissionStateModuleMock(() => mockConsoleState)
@@ -14,9 +14,9 @@ import { useCallback, useMemo, useState } from 'react'
import { useTranslation } from 'react-i18next'
import AccessRulesEditor from '@/app/components/access-rules-editor'
import { useStore } from '@/app/components/app/store'
import { userProfileIdAtom } from '@/context/account-state'
import { useLocale } from '@/context/i18n'
import { workspacePermissionKeysAtom } from '@/context/permission-state'
import { userProfileQueryOptions } from '@/features/account-profile/client'
import { systemFeaturesQueryOptions } from '@/features/system-features/client'
import { getAccessControlTemplateLanguage } from '@/i18n-config/language'
import {
@@ -134,7 +134,10 @@ const AppAccessConfigContent = ({ appId, maintainerId }: AppAccessConfigContentP
const AppAccessConfigPage = ({ appId }: AppAccessConfigPageProps) => {
const { data: systemFeatures } = useSuspenseQuery(systemFeaturesQueryOptions())
const currentUserId = useAtomValue(userProfileIdAtom)
const { data: currentUserId } = useSuspenseQuery({
...userProfileQueryOptions(),
select: (data) => data.profile.id,
})
const workspacePermissionKeys = useAtomValue(workspacePermissionKeysAtom)
const isRbacEnabled = systemFeatures.rbac_enabled
const appDetail = useStore((state) => state.appDetail)
@@ -53,21 +53,6 @@ vi.mock('@/service/client', async (importOriginal) => {
}
})
vi.mock('@/context/account-state', async () => {
const { atom } = await vi.importActual<typeof import('jotai')>('jotai')
return {
userProfileAtom: atom({
id: 'user-1',
name: 'Test User',
email: 'test@dify.ai',
avatar: '',
avatar_url: null,
is_password_set: false,
timezone: 'UTC',
}),
}
})
vi.mock('ahooks', async (importOriginal) => {
const actual = await importOriginal<typeof import('ahooks')>()
return {
@@ -1,4 +1,5 @@
'use client'
import type { ComboboxChangeEventDetails } from '@langgenius/dify-ui/combobox'
import type {
AccessControlAccount,
@@ -23,11 +24,11 @@ import {
ComboboxTrigger,
} from '@langgenius/dify-ui/combobox'
import { RiArrowRightSLine, RiOrganizationChart } from '@remixicon/react'
import { useSuspenseQuery } from '@tanstack/react-query'
import { useDebounce } from 'ahooks'
import { useAtomValue } from 'jotai'
import { useEffect, useRef, useState } from 'react'
import { useTranslation } from 'react-i18next'
import { userProfileAtom } from '@/context/account-state'
import { userProfileQueryOptions } from '@/features/account-profile/client'
import { SubjectType } from '@/models/access-control'
import { useSearchForWhiteListCandidates } from '@/service/access-control'
import useAccessControlStore from '../../../../context/access-control-store'
@@ -337,7 +338,10 @@ type MemberItemProps = {
subject: Subject
}
function MemberItem({ member, subject }: MemberItemProps) {
const currentUser = useAtomValue(userProfileAtom)
const { data: currentUser } = useSuspenseQuery({
...userProfileQueryOptions(),
select: (data) => data.profile,
})
const { t } = useTranslation()
return (
<BaseItem subject={subject} className="pr-3">
@@ -10,11 +10,17 @@ import {
} from '@/app/components/workflow/nodes/knowledge-retrieval/types'
import { getSelectedDatasetsMode } from '@/app/components/workflow/nodes/knowledge-retrieval/utils'
import { DatasetPermission, DataSourceType } from '@/models/datasets'
import { render } from '@/test/console/render'
import { createConsoleQueryWrapper } from '@/test/console/query-data'
import { render as renderWithConsoleState } from '@/test/console/render'
import { AppModeEnum, ModelModeType, RETRIEVE_TYPE } from '@/types/app'
import { DatasetACLPermission, getDatasetACLCapabilities } from '@/utils/permission'
import DatasetConfig from '../index'
const render = (ui: Parameters<typeof renderWithConsoleState>[0]) =>
renderWithConsoleState(ui, {
wrapper: createConsoleQueryWrapper({ accountProfile: { id: 'user-123' } }).wrapper,
})
// Mock external dependencies
vi.mock('@/app/components/workflow/nodes/knowledge-retrieval/utils', () => ({
getMultipleRetrievalConfig: vi.fn(() => ({
@@ -41,13 +47,6 @@ vi.mock('@/app/components/header/account-setting/model-provider-page/hooks', ()
})),
}))
vi.mock('@/context/account-state', async () => {
const { createAccountStateModuleMock } = await import('@/test/console/state-fixture')
return createAccountStateModuleMock(() => ({
userProfile: { id: 'user-123' },
workspacePermissionKeys: [],
}))
})
vi.mock('@/context/permission-state', async () => {
const { createPermissionStateModuleMock } = await import('@/test/console/state-fixture')
return createPermissionStateModuleMock(() => ({
@@ -1,4 +1,5 @@
'use client'
import type { FC } from 'react'
import type {
HandleAddCondition,
@@ -9,6 +10,7 @@ import type {
} from '@/app/components/workflow/nodes/knowledge-retrieval/types'
import type { DataSet } from '@/models/datasets'
import { cn } from '@langgenius/dify-ui/cn'
import { useSuspenseQuery } from '@tanstack/react-query'
import { intersectionBy } from 'es-toolkit/compat'
import { produce } from 'immer'
import { useAtomValue } from 'jotai'
@@ -29,9 +31,9 @@ import {
getMultipleRetrievalConfig,
getSelectedDatasetsMode,
} from '@/app/components/workflow/nodes/knowledge-retrieval/utils'
import { userProfileIdAtom } from '@/context/account-state'
import ConfigContext from '@/context/debug-configuration'
import { workspacePermissionKeysAtom } from '@/context/permission-state'
import { userProfileQueryOptions } from '@/features/account-profile/client'
import { AppModeEnum } from '@/types/app'
import { getDatasetACLCapabilities } from '@/utils/permission'
import FeaturePanel from '../base/feature-panel'
@@ -47,7 +49,10 @@ type Props = Readonly<{
}>
const DatasetConfig: FC<Props> = ({ readonly, hideMetadataFilter }) => {
const { t } = useTranslation()
const currentUserId = useAtomValue(userProfileIdAtom)
const { data: currentUserId } = useSuspenseQuery({
...userProfileQueryOptions(),
select: (data) => data.profile.id,
})
const workspacePermissionKeys = useAtomValue(workspacePermissionKeysAtom)
const {
mode,
@@ -3,7 +3,7 @@ import type { ModelAndParameter } from '../types'
import type { InputForm } from '@/app/components/base/chat/chat/type'
import type { ChatConfig, OnSend } from '@/app/components/base/chat/types'
import { Avatar } from '@langgenius/dify-ui/avatar'
import { useAtomValue } from 'jotai'
import { useSuspenseQuery } from '@tanstack/react-query'
import { memo, useCallback, useMemo } from 'react'
import { toast } from '@/app/components/app/configuration/toast'
import Chat from '@/app/components/base/chat/chat'
@@ -11,10 +11,10 @@ import { useChat } from '@/app/components/base/chat/chat/hooks'
import { getLastAnswer } from '@/app/components/base/chat/utils'
import { useFeatures } from '@/app/components/base/features/hooks'
import { ModelFeatureEnum } from '@/app/components/header/account-setting/model-provider-page/declarations'
import { userProfileAtom } from '@/context/account-state'
import { useDebugConfigurationContext } from '@/context/debug-configuration'
import { useEventEmitterContextContext } from '@/context/event-emitter'
import { useProviderContext } from '@/context/provider-context'
import { userProfileQueryOptions } from '@/features/account-profile/client'
import {
fetchConversationMessages,
fetchSuggestedQuestions,
@@ -28,7 +28,10 @@ type ChatItemProps = {
modelAndParameter: ModelAndParameter
}
const ChatItem: FC<ChatItemProps> = ({ modelAndParameter }) => {
const userProfile = useAtomValue(userProfileAtom)
const { data: userProfile } = useSuspenseQuery({
...userProfileQueryOptions(),
select: (data) => data.profile,
})
const {
modelConfig,
appId,
@@ -2,7 +2,7 @@ import type { InputForm } from '@/app/components/base/chat/chat/type'
import type { ChatConfig, ChatItem, OnSend } from '@/app/components/base/chat/types'
import type { FileEntity } from '@/app/components/base/file-uploader/types'
import { Avatar } from '@langgenius/dify-ui/avatar'
import { useAtomValue } from 'jotai'
import { useSuspenseQuery } from '@tanstack/react-query'
import { memo, useCallback, useImperativeHandle, useMemo } from 'react'
import { toast } from '@/app/components/app/configuration/toast'
import { useStore as useAppStore } from '@/app/components/app/store'
@@ -10,9 +10,9 @@ import Chat from '@/app/components/base/chat/chat'
import { useChat } from '@/app/components/base/chat/chat/hooks'
import { getLastAnswer, isValidGeneratedAnswer } from '@/app/components/base/chat/utils'
import { useFeatures } from '@/app/components/base/features/hooks'
import { userProfileAtom } from '@/context/account-state'
import { useDebugConfigurationContext } from '@/context/debug-configuration'
import { useProviderContext } from '@/context/provider-context'
import { userProfileQueryOptions } from '@/features/account-profile/client'
import {
fetchConversationMessages,
fetchSuggestedQuestions,
@@ -33,7 +33,10 @@ const DebugWithSingleModel = ({
}: DebugWithSingleModelProps & {
ref: React.RefObject<DebugWithSingleModelRefType>
}) => {
const userProfile = useAtomValue(userProfileAtom)
const { data: userProfile } = useSuspenseQuery({
...userProfileQueryOptions(),
select: (data) => data.profile,
})
const {
readonly,
canTestAndRun = false,
@@ -2,6 +2,7 @@
import { act, waitFor } from '@testing-library/react'
import { updateAppModelConfig } from '@/service/apps'
import { consoleQuery } from '@/service/client'
import { seedAccountProfileQuery } from '@/test/console/account-profile'
import { createQueryClientWrapper } from '@/test/console/query-client'
import { renderHook as renderHookWithConsoleState } from '@/test/console/render'
import { createTestQueryClient } from '@/test/query-client'
@@ -11,6 +12,7 @@ import { useConfiguration } from '../use-configuration'
const renderHook = (callback: () => ReturnType<typeof useConfiguration>) => {
const queryClient = createTestQueryClient()
seedAccountProfileQuery(queryClient, { id: 'user-1' })
return {
...renderHookWithConsoleState(callback, {
wrapper: createQueryClientWrapper(queryClient),
@@ -58,15 +60,6 @@ vi.mock('ahooks', async () => {
}
})
vi.mock('@/context/account-state', async () => {
const { createAccountStateModuleMock } = await import('@/test/console/state-fixture')
return createAccountStateModuleMock(() => ({
currentWorkspace: { id: 'workspace-1' },
isLoadingCurrentWorkspace: false,
userProfile: { id: 'user-1' },
workspacePermissionKeys: ['app.create_and_management'],
}))
})
vi.mock('@/context/workspace-state', async () => {
const { createWorkspaceStateModuleMock } = await import('@/test/console/state-fixture')
return createWorkspaceStateModuleMock(() => ({
@@ -25,7 +25,7 @@ import type {
TextToSpeechConfig,
} from '@/models/debug'
import type { VisionSettings } from '@/types/app'
import { useMutation } from '@tanstack/react-query'
import { useMutation, useSuspenseQuery } from '@tanstack/react-query'
import { useBoolean, useGetState } from 'ahooks'
import { clone } from 'es-toolkit/object'
import { produce } from 'immer'
@@ -60,10 +60,10 @@ import {
DEFAULT_CHAT_PROMPT_CONFIG,
DEFAULT_COMPLETION_PROMPT_CONFIG,
} from '@/config'
import { userProfileIdAtom } from '@/context/account-state'
import { workspacePermissionKeysAtom } from '@/context/permission-state'
import { useProviderContext } from '@/context/provider-context'
import { currentWorkspaceAtom, currentWorkspaceLoadingAtom } from '@/context/workspace-state'
import { userProfileQueryOptions } from '@/features/account-profile/client'
import useBreakpoints, { MediaType } from '@/hooks/use-breakpoints'
import { PromptMode } from '@/models/debug'
import { usePathname } from '@/next/navigation'
@@ -131,7 +131,10 @@ export const useConfiguration = (): ConfigurationViewModel => {
const { t } = useTranslation()
const isLoadingCurrentWorkspace = useAtomValue(currentWorkspaceLoadingAtom)
const currentWorkspace = useAtomValue(currentWorkspaceAtom)
const currentUserId = useAtomValue(userProfileIdAtom)
const { data: currentUserId } = useSuspenseQuery({
...userProfileQueryOptions(),
select: (data) => data.profile.id,
})
const workspacePermissionKeys = useAtomValue(workspacePermissionKeysAtom)
const [, setSettingsDestination] = useQueryState(settingsQueryParamName, settingsQueryParser)
@@ -27,13 +27,6 @@ vi.mock('ahooks', () => ({
},
}))
vi.mock('@/context/account-state', async () => {
const { createAccountStateModuleMock } = await import('@/test/console/state-fixture')
return createAccountStateModuleMock(() => ({
userProfile: mockUserProfile,
workspacePermissionKeys: mockWorkspacePermissionKeys,
}))
})
vi.mock('@/context/permission-state', async () => {
const { createPermissionStateModuleMock } = await import('@/test/console/state-fixture')
return createPermissionStateModuleMock(() => ({
@@ -156,6 +149,13 @@ vi.mock('@/service/client', async (importOriginal) => {
...actual,
consoleQuery: {
...actual.consoleQuery,
account: {
profile: {
get: {
queryKey: () => [['console', 'account', 'profile', 'get'], { type: 'query' }],
},
},
},
systemFeatures: actual.consoleQuery.systemFeatures,
apps: {
...actual.consoleQuery.apps,
@@ -16,8 +16,8 @@ import Divider from '@/app/components/base/divider'
import Loading from '@/app/components/base/loading'
import CreateAppModal from '@/app/components/explore/create-app-modal'
import { usePluginDependencies } from '@/app/components/workflow/plugin-dependency/hooks'
import { userProfileIdAtom } from '@/context/account-state'
import { workspacePermissionKeysAtom } from '@/context/permission-state'
import { userProfileQueryOptions } from '@/features/account-profile/client'
import { systemFeaturesQueryOptions } from '@/features/system-features/client'
import { useRouter } from '@/next/navigation'
import { consoleQuery } from '@/service/client'
@@ -43,7 +43,10 @@ type AppsProps = {
const Apps = ({ onSuccess, onCreateFromBlank }: AppsProps) => {
const { t } = useTranslation()
const { data: systemFeatures } = useSuspenseQuery(systemFeaturesQueryOptions())
const currentUserId = useAtomValue(userProfileIdAtom)
const { data: currentUserId } = useSuspenseQuery({
...userProfileQueryOptions(),
select: (data) => data.profile.id,
})
const workspacePermissionKeys = useAtomValue(workspacePermissionKeysAtom)
const isRbacEnabled = systemFeatures.rbac_enabled
const canCreateAppFromTemplate = hasPermission(
@@ -48,6 +48,13 @@ vi.mock('@/service/client', async (importOriginal) => {
...actual,
consoleQuery: {
...actual.consoleQuery,
account: {
profile: {
get: {
queryKey: () => [['console', 'account', 'profile', 'get'], { type: 'query' }],
},
},
},
systemFeatures: actual.consoleQuery.systemFeatures,
apps: {
...actual.consoleQuery.apps,
@@ -86,10 +93,7 @@ vi.mock('@/utils/app-redirection', () => ({
vi.mock('@/context/provider-context', () => ({
useProviderContext: vi.fn(),
}))
vi.mock('@/context/account-state', async () => {
const { createAccountStateModuleMock } = await import('@/test/console/state-fixture')
return createAccountStateModuleMock(() => mockConsoleState)
})
vi.mock('@/context/permission-state', async () => {
const { createPermissionStateModuleMock } = await import('@/test/console/state-fixture')
return createPermissionStateModuleMock(() => mockConsoleState)
@@ -18,9 +18,9 @@ import { useTranslation } from 'react-i18next'
import AppIcon from '@/app/components/base/app-icon'
import Divider from '@/app/components/base/divider'
import AppsFull from '@/app/components/billing/apps-full-in-dialog'
import { userProfileIdAtom } from '@/context/account-state'
import { workspacePermissionKeysAtom } from '@/context/permission-state'
import { useProviderContext } from '@/context/provider-context'
import { userProfileQueryOptions } from '@/features/account-profile/client'
import { systemFeaturesQueryOptions } from '@/features/system-features/client'
import useTheme from '@/hooks/use-theme'
import { useRouter } from '@/next/navigation'
@@ -70,7 +70,10 @@ function CreateApp({ onClose, onSuccess, onCreateFromTemplate, defaultAppMode }:
const { plan, enableBilling } = useProviderContext()
const isAppsFull = enableBilling && plan.usage.buildApps >= plan.total.buildApps
const { data: systemFeatures } = useSuspenseQuery(systemFeaturesQueryOptions())
const currentUserId = useAtomValue(userProfileIdAtom)
const { data: currentUserId } = useSuspenseQuery({
...userProfileQueryOptions(),
select: (data) => data.profile.id,
})
const workspacePermissionKeys = useAtomValue(workspacePermissionKeysAtom)
const isRbacEnabled = systemFeatures.rbac_enabled
const canCreateApp = hasPermission(workspacePermissionKeys, 'app.create_and_management')
@@ -76,6 +76,13 @@ vi.mock('@/service/client', async (importOriginal) => {
},
consoleQuery: {
...actual.consoleQuery,
account: {
profile: {
get: {
queryKey: () => [['console', 'account', 'profile', 'get'], { type: 'query' }],
},
},
},
systemFeatures: actual.consoleQuery.systemFeatures,
apps: {
...actual.consoleQuery.apps,
@@ -107,13 +114,6 @@ vi.mock('@/app/components/workflow/plugin-dependency/hooks', () => ({
}),
}))
vi.mock('@/context/account-state', async () => {
const { createAccountStateModuleMock } = await import('@/test/console/state-fixture')
return createAccountStateModuleMock(() => ({
userProfile: mockUserProfile,
workspacePermissionKeys: mockWorkspacePermissionKeys,
}))
})
vi.mock('@/context/permission-state', async () => {
const { createPermissionStateModuleMock } = await import('@/test/console/state-fixture')
return createPermissionStateModuleMock(() => ({
@@ -23,9 +23,9 @@ import { useRef, useState } from 'react'
import { useTranslation } from 'react-i18next'
import AppsFull from '@/app/components/billing/apps-full-in-dialog'
import { usePluginDependencies } from '@/app/components/workflow/plugin-dependency/hooks'
import { userProfileIdAtom } from '@/context/account-state'
import { workspacePermissionKeysAtom } from '@/context/permission-state'
import { useProviderContext } from '@/context/provider-context'
import { userProfileQueryOptions } from '@/features/account-profile/client'
import { systemFeaturesQueryOptions } from '@/features/system-features/client'
import { useRouter } from '@/next/navigation'
import { consoleQuery } from '@/service/client'
@@ -121,7 +121,10 @@ function CreateFromDSLModal({
)
const { handleCheckPluginDependencies } = usePluginDependencies()
const { data: systemFeatures } = useSuspenseQuery(systemFeaturesQueryOptions())
const currentUserId = useAtomValue(userProfileIdAtom)
const { data: currentUserId } = useSuspenseQuery({
...userProfileQueryOptions(),
select: (data) => data.profile.id,
})
const workspacePermissionKeys = useAtomValue(workspacePermissionKeysAtom)
const { plan, enableBilling } = useProviderContext()
const isAppsFull = enableBilling && plan.usage.buildApps >= plan.total.buildApps
+5 -2
View File
@@ -5,8 +5,8 @@ import { useSuspenseQuery } from '@tanstack/react-query'
import { useAtomValue } from 'jotai'
import * as React from 'react'
import { Trans, useTranslation } from 'react-i18next'
import { userProfileIdAtom } from '@/context/account-state'
import { workspacePermissionKeysAtom } from '@/context/permission-state'
import { userProfileQueryOptions } from '@/features/account-profile/client'
import { systemFeaturesQueryOptions } from '@/features/system-features/client'
import Link from '@/next/link'
import { AppModeEnum } from '@/types/app'
@@ -36,7 +36,10 @@ const ThreeDotsIcon = ({ className }: SVGProps<SVGElement>) => {
const EmptyElement: FC<{ appDetail: App }> = ({ appDetail }) => {
const { t } = useTranslation()
const currentUserId = useAtomValue(userProfileIdAtom)
const { data: currentUserId } = useSuspenseQuery({
...userProfileQueryOptions(),
select: (data) => data.profile.id,
})
const workspacePermissionKeys = useAtomValue(workspacePermissionKeysAtom)
const { data: systemFeatures } = useSuspenseQuery(systemFeaturesQueryOptions())
const isRbacEnabled = systemFeatures.rbac_enabled
+5 -2
View File
@@ -14,9 +14,9 @@ import { useTranslation } from 'react-i18next'
import AppBasic from '@/app/components/app-sidebar/basic'
import { useStore as useAppStore } from '@/app/components/app/store'
import SecretKeyButton from '@/app/components/develop/secret-key/secret-key-button'
import { userProfileIdAtom } from '@/context/account-state'
import { useDocLink } from '@/context/i18n'
import { workspacePermissionKeysAtom } from '@/context/permission-state'
import { userProfileQueryOptions } from '@/features/account-profile/client'
import { systemFeaturesQueryOptions } from '@/features/system-features/client'
import { AccessMode } from '@/models/access-control'
import { usePathname, useRouter } from '@/next/navigation'
@@ -71,7 +71,10 @@ function AppCard({
}: IAppCardProps) {
const router = useRouter()
const pathname = usePathname()
const currentUserId = useAtomValue(userProfileIdAtom)
const { data: currentUserId } = useSuspenseQuery({
...userProfileQueryOptions(),
select: (data) => data.profile.id,
})
const workspacePermissionKeys = useAtomValue(workspacePermissionKeysAtom)
const appACLCapabilities = useMemo(
() =>
@@ -1,4 +1,5 @@
'use client'
import type { TriggerWithProvider } from '@/app/components/workflow/block-selector/types'
import type { AppDetailResponse } from '@/models/app'
import type { AppTrigger } from '@/service/use-tools'
@@ -6,15 +7,16 @@ import type { AppSSO } from '@/types/app'
import type { I18nKeysByPrefix } from '@/types/i18n'
import { StatusDot } from '@langgenius/dify-ui/status-dot'
import { Switch } from '@langgenius/dify-ui/switch'
import { useSuspenseQuery } from '@tanstack/react-query'
import { useAtomValue } from 'jotai'
import * as React from 'react'
import { useTranslation } from 'react-i18next'
import BlockIcon from '@/app/components/workflow/block-icon'
import { useTriggerStatusStore } from '@/app/components/workflow/store/trigger-status'
import { BlockEnum } from '@/app/components/workflow/types'
import { userProfileIdAtom } from '@/context/account-state'
import { useDocLink } from '@/context/i18n'
import { workspacePermissionKeysAtom } from '@/context/permission-state'
import { userProfileQueryOptions } from '@/features/account-profile/client'
import Link from '@/next/link'
import {
useAppTriggers,
@@ -77,7 +79,10 @@ function TriggerCard({ appInfo, onToggleResult }: ITriggerCardProps) {
const { t } = useTranslation()
const docLink = useDocLink()
const appId = appInfo.id
const currentUserId = useAtomValue(userProfileIdAtom)
const { data: currentUserId } = useSuspenseQuery({
...userProfileQueryOptions(),
select: (data) => data.profile.id,
})
const workspacePermissionKeys = useAtomValue(workspacePermissionKeysAtom)
const canEditApp = React.useMemo(
() =>
@@ -103,15 +103,6 @@ vi.mock('@/app/components/app/use-export-app-dsl', () => ({
useExportWorkflowAppDsl: () => mockWorkflowAppDslExport,
}))
const render = (ui: React.ReactElement) =>
renderWithConsoleQuery(ui, {
systemFeatures: {
webapp_auth: { enabled: mockWebappAuthEnabled },
branding: { enabled: false },
rbac_enabled: mockRbacEnabled,
},
})
const getOperationsTrigger = () =>
screen.getByRole('button', { name: /common\.operation\.moreActionsFor/ })
@@ -173,12 +164,18 @@ const mockConsoleState = vi.hoisted(() => ({
workspacePermissionKeys: ['app.create_and_management'] as string[],
}))
const render = (ui: React.ReactElement) =>
renderWithConsoleQuery(ui, {
accountProfile: mockConsoleState.userProfile,
systemFeatures: {
webapp_auth: { enabled: mockWebappAuthEnabled },
branding: { enabled: false },
rbac_enabled: mockRbacEnabled,
},
})
// Mock app context
vi.mock('@/context/account-state', async () => {
const { createAccountStateModuleMock } = await import('@/test/console/state-fixture')
return createAccountStateModuleMock(() => mockConsoleState)
})
vi.mock('@/context/workspace-state', async () => {
const { createWorkspaceStateModuleMock } = await import('@/test/console/state-fixture')
return createWorkspaceStateModuleMock(() => mockConsoleState)
@@ -1,15 +1,14 @@
import { fireEvent, screen, within } from '@testing-library/react'
import { render } from '@/test/console/render'
import { createConsoleQueryWrapper } from '@/test/console/query-data'
import { render as renderWithConsoleState } from '@/test/console/render'
import CreatorsFilter from '../creators-filter'
const mockOnChange = vi.hoisted(() => vi.fn())
vi.mock('@/context/account-state', async () => {
const { createAccountStateModuleMock } = await import('@/test/console/state-fixture')
return createAccountStateModuleMock(() => ({
userProfile: { id: 'member-2' },
}))
})
const render = (ui: Parameters<typeof renderWithConsoleState>[0]) =>
renderWithConsoleState(ui, {
wrapper: createConsoleQueryWrapper({ accountProfile: { id: 'member-2' } }).wrapper,
})
vi.mock('@/service/use-common', () => ({
useMembers: () => ({
@@ -86,6 +86,13 @@ vi.mock('@/service/client', () => ({
},
},
consoleQuery: {
account: {
profile: {
get: {
queryKey: () => [['console', 'account', 'profile', 'get'], { type: 'query' }],
},
},
},
apps: {
get: {
key: () => ['console', 'apps', 'get'],
@@ -117,13 +124,6 @@ vi.mock('@/service/client', () => ({
let mockWorkspacePermissionKeys = ['app.create_and_management']
vi.mock('@/context/account-state', async () => {
const { createAccountStateModuleMock } = await import('@/test/console/state-fixture')
return createAccountStateModuleMock(() => ({
userProfile: { id: 'creator-1' },
workspacePermissionKeys: mockWorkspacePermissionKeys,
}))
})
vi.mock('@/context/permission-state', async () => {
const { createPermissionStateModuleMock } = await import('@/test/console/state-fixture')
return createPermissionStateModuleMock(() => ({
@@ -502,6 +502,7 @@ type RenderListOptions = {
const renderList = (searchParams = '', options: RenderListOptions = {}) => {
mockSearchParams = new URLSearchParams(searchParams)
const { wrapper: ConsoleQueryWrapper, systemFeatures } = createConsoleQueryWrapper({
accountProfile: { id: 'creator-1' },
systemFeatures: { branding: { enabled: false }, ...options.systemFeatures },
})
mockSystemFeatures = systemFeatures
+9 -3
View File
@@ -43,9 +43,9 @@ import {
getStepByStepTourDropdownMenuContentProps,
useStepByStepTourControlledDropdown,
} from '@/app/components/step-by-step-tour/dropdown-menu'
import { userProfileIdAtom } from '@/context/account-state'
import { workspacePermissionKeysAtom } from '@/context/permission-state'
import { useProviderContext } from '@/context/provider-context'
import { userProfileQueryOptions } from '@/features/account-profile/client'
import { systemFeaturesQueryOptions } from '@/features/system-features/client'
import { AppCardTags } from '@/features/tag-management/components/app-card-tags'
import { useAsyncWindowOpen } from '@/hooks/use-async-window-open'
@@ -361,7 +361,10 @@ export const AppCardActionBar = memo(
}: AppCardActionBarProps) => {
const { t } = useTranslation()
const { data: systemFeatures } = useSuspenseQuery(systemFeaturesQueryOptions())
const currentUserId = useAtomValue(userProfileIdAtom)
const { data: currentUserId } = useSuspenseQuery({
...userProfileQueryOptions(),
select: (data) => data.profile.id,
})
const workspacePermissionKeys = useAtomValue(workspacePermissionKeysAtom)
const isRbacEnabled = systemFeatures.rbac_enabled
const { onPlanInfoChanged } = useProviderContext()
@@ -864,7 +867,10 @@ export const AppCard = memo(
}: AppCardProps) => {
const { t } = useTranslation()
const { data: systemFeatures } = useSuspenseQuery(systemFeaturesQueryOptions())
const currentUserId = useAtomValue(userProfileIdAtom)
const { data: currentUserId } = useSuspenseQuery({
...userProfileQueryOptions(),
select: (data) => data.profile.id,
})
const workspacePermissionKeys = useAtomValue(workspacePermissionKeysAtom)
const isRbacEnabled = systemFeatures.rbac_enabled
const resourceMaintainer = app.maintainer ?? undefined
+6 -3
View File
@@ -9,10 +9,10 @@ import {
DropdownMenuTrigger,
} from '@langgenius/dify-ui/dropdown-menu'
import { Input } from '@langgenius/dify-ui/input'
import { useAtomValue } from 'jotai'
import { useSuspenseQuery } from '@tanstack/react-query'
import { useCallback, useMemo, useState } from 'react'
import { useTranslation } from 'react-i18next'
import { userProfileIdAtom } from '@/context/account-state'
import { userProfileQueryOptions } from '@/features/account-profile/client'
import { useMembers } from '@/service/use-common'
type CreatorsFilterProps = {
@@ -32,7 +32,10 @@ const baseChipClassName =
const CreatorsFilter = ({ value, onChange }: CreatorsFilterProps) => {
const { t } = useTranslation()
const currentUserId = useAtomValue(userProfileIdAtom)
const { data: currentUserId } = useSuspenseQuery({
...userProfileQueryOptions(),
select: (data) => data.profile.id,
})
const { data: membersData } = useMembers()
const [keywords, setKeywords] = useState('')
+5 -2
View File
@@ -10,8 +10,8 @@ import { memo, useCallback, useId, useMemo } from 'react'
import { useTranslation } from 'react-i18next'
import { AppTypeIcon } from '@/app/components/app/type-selector'
import AppIcon from '@/app/components/base/app-icon'
import { userProfileIdAtom } from '@/context/account-state'
import { workspacePermissionKeysAtom } from '@/context/permission-state'
import { userProfileQueryOptions } from '@/features/account-profile/client'
import { systemFeaturesQueryOptions } from '@/features/system-features/client'
import Link from '@/next/link'
import { getRedirectionPath } from '@/utils/app-redirection'
@@ -28,7 +28,10 @@ type StarredAppCardProps = {
export const StarredAppCard = memo(
({ app, stepByStepTourCardTarget, stepByStepTourCardHighlightPart }: StarredAppCardProps) => {
const { t } = useTranslation()
const currentUserId = useAtomValue(userProfileIdAtom)
const { data: currentUserId } = useSuspenseQuery({
...userProfileQueryOptions(),
select: (data) => data.profile.id,
})
const workspacePermissionKeys = useAtomValue(workspacePermissionKeysAtom)
const { data: systemFeatures } = useSuspenseQuery(systemFeaturesQueryOptions())
const isRbacEnabled = systemFeatures.rbac_enabled
@@ -1,10 +1,10 @@
import type { AvatarSize } from '@langgenius/dify-ui/avatar'
import type { FC } from 'react'
import { AvatarFallback, AvatarImage, AvatarRoot } from '@langgenius/dify-ui/avatar'
import { useAtomValue } from 'jotai'
import { useSuspenseQuery } from '@tanstack/react-query'
import { memo } from 'react'
import { getUserColor } from '@/app/components/workflow/collaboration/utils/user-color'
import { userProfileIdAtom } from '@/context/account-state'
import { userProfileQueryOptions } from '@/features/account-profile/client'
type User = {
id: string
@@ -33,7 +33,10 @@ const avatarSizeToPx: Record<AvatarSize, number> = {
export const UserAvatarList: FC<UserAvatarListProps> = memo(
({ users, maxVisible = 3, size = 'sm', className = '', showCount = true }) => {
const currentUserId = useAtomValue(userProfileIdAtom)
const { data: currentUserId } = useSuspenseQuery({
...userProfileQueryOptions(),
select: (data) => data.profile.id,
})
if (!users.length) return null
const shouldShowCount = showCount && users.length > maxVisible
+7 -2
View File
@@ -1,11 +1,13 @@
'use client'
import { useSuspenseQuery } from '@tanstack/react-query'
import { useAtomValue } from 'jotai'
import { useStore as useAppStore } from '@/app/components/app/store'
import Loading from '@/app/components/base/loading'
import ApiServer from '@/app/components/develop/ApiServer'
import Doc from '@/app/components/develop/doc'
import { userProfileIdAtom } from '@/context/account-state'
import { workspacePermissionKeysAtom } from '@/context/permission-state'
import { userProfileQueryOptions } from '@/features/account-profile/client'
import { getAppACLCapabilities } from '@/utils/permission'
type IDevelopMainProps = {
@@ -14,7 +16,10 @@ type IDevelopMainProps = {
const DevelopMain = ({ appId }: IDevelopMainProps) => {
const appDetail = useAppStore((state) => state.appDetail)
const currentUserId = useAtomValue(userProfileIdAtom)
const { data: currentUserId } = useSuspenseQuery({
...userProfileQueryOptions(),
select: (data) => data.profile.id,
})
const workspacePermissionKeys = useAtomValue(workspacePermissionKeysAtom)
const appACLCapabilities = getAppACLCapabilities(appDetail?.permission_keys, {
currentUserId,
@@ -56,6 +56,13 @@ vi.mock('@/service/client', () => ({
systemFeatures: vi.fn(),
},
consoleQuery: {
account: {
profile: {
get: {
queryKey: () => [['console', 'account', 'profile', 'get'], { type: 'query' }],
},
},
},
tags: {
list: {
queryOptions: (options: unknown) => options,
@@ -242,6 +249,7 @@ const renderList = ({
brandingEnabled?: boolean
} = {}) => {
const { wrapper: ConsoleQueryWrapper } = createConsoleQueryWrapper({
accountProfile: { id: 'creator-1' },
systemFeatures: { branding: { enabled: brandingEnabled } },
})
@@ -3,7 +3,8 @@ import { cleanup, fireEvent, screen } from '@testing-library/react'
import * as React from 'react'
import { act } from 'react'
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
import { render } from '@/test/console/render'
import { createConsoleQueryWrapper } from '@/test/console/query-data'
import { render as renderWithConsoleState } from '@/test/console/render'
import { Banner } from '../banner'
const mockTrackEvent = vi.fn()
@@ -22,6 +23,11 @@ const mockConsoleState = vi.hoisted(() => ({
},
}))
const render = (ui: Parameters<typeof renderWithConsoleState>[0]) =>
renderWithConsoleState(ui, {
wrapper: createConsoleQueryWrapper({ accountProfile: mockConsoleState.userProfile }).wrapper,
})
const emitAutoplay = (event: 'play' | 'stop') => {
mockAutoplayListeners[event].forEach((listener) => listener())
}
@@ -58,11 +64,6 @@ const setMockSelectedIndex = (index: number) => {
mockCarouselListeners.forEach((listener) => listener())
}
vi.mock('@/context/account-state', async () => {
const { createAccountStateModuleMock } = await import('@/test/console/state-fixture')
return createAccountStateModuleMock(() => mockConsoleState)
})
vi.mock('@/app/components/base/amplitude', () => ({
trackEvent: (...args: unknown[]) => mockTrackEvent(...args),
}))
+6 -3
View File
@@ -1,12 +1,12 @@
import type { BannerResponse } from '@dify/contracts/api/console/explore/types.gen'
import type { ComponentProps, FocusEvent } from 'react'
import { useAtomValue } from 'jotai'
import { useSuspenseQuery } from '@tanstack/react-query'
import { useEffect, useId, useRef, useState } from 'react'
import { useTranslation } from 'react-i18next'
import { trackEvent } from '@/app/components/base/amplitude'
import { Carousel, useCarousel } from '@/app/components/base/carousel'
import { userProfileAtom } from '@/context/account-state'
import { useLocale } from '@/context/i18n'
import { userProfileQueryOptions } from '@/features/account-profile/client'
import { BannerItem } from './banner-item'
import { IndicatorButton } from './indicator-button'
@@ -190,7 +190,10 @@ type BannerProps = {
export function Banner({ banners }: BannerProps) {
const { t } = useTranslation()
const locale = useLocale()
const userProfile = useAtomValue(userProfileAtom)
const { data: userProfile } = useSuspenseQuery({
...userProfileQueryOptions(),
select: (data) => data.profile,
})
const [carouselPlugins] = useState(() => [
Carousel.Plugin.Fade(),
Carousel.Plugin.Autoplay({
@@ -17,10 +17,6 @@ const toastMocks = vi.hoisted(() => ({
warning: vi.fn(),
}))
vi.mock('@/context/account-state', async () => {
const { createAccountStateModuleMock } = await import('@/test/console/state-fixture')
return createAccountStateModuleMock(() => mockConsoleState)
})
vi.mock('@/context/permission-state', async () => {
const { createPermissionStateModuleMock } = await import('@/test/console/state-fixture')
return createPermissionStateModuleMock(() => mockConsoleState)
@@ -81,7 +77,11 @@ const renderItem = (
systemFeatures: NonNullable<Parameters<typeof renderWithConsoleQuery>[1]>['systemFeatures'] = {
rbac_enabled: true,
},
) => renderWithConsoleQuery(<ContinueWorkItem app={app} />, { systemFeatures })
) =>
renderWithConsoleQuery(<ContinueWorkItem app={app} />, {
accountProfile: mockConsoleState.userProfile,
systemFeatures,
})
describe('ContinueWorkItem', () => {
beforeEach(() => {
+5 -2
View File
@@ -9,8 +9,8 @@ import { useId, useState } from 'react'
import { useTranslation } from 'react-i18next'
import { AppTypeIcon } from '@/app/components/app/type-selector'
import AppIcon from '@/app/components/base/app-icon'
import { userProfileIdAtom } from '@/context/account-state'
import { workspacePermissionKeysAtom } from '@/context/permission-state'
import { userProfileQueryOptions } from '@/features/account-profile/client'
import { systemFeaturesQueryOptions } from '@/features/system-features/client'
import { useFormatTimeFromNow } from '@/hooks/use-format-time-from-now'
import Link from '@/next/link'
@@ -32,7 +32,10 @@ type ContinueWorkItemProps = {
export function ContinueWorkItem({ app }: ContinueWorkItemProps) {
const { t } = useTranslation()
const { formatTimeFromNow } = useFormatTimeFromNow()
const currentUserId = useAtomValue(userProfileIdAtom)
const { data: currentUserId } = useSuspenseQuery({
...userProfileQueryOptions(),
select: (data) => data.profile.id,
})
const workspacePermissionKeys = useAtomValue(workspacePermissionKeysAtom)
const { data: systemFeatures } = useSuspenseQuery(systemFeaturesQueryOptions())
const appNameId = useId()
@@ -224,6 +224,13 @@ vi.mock('@/service/client', () => ({
systemFeatures: () => Promise.resolve({}),
},
consoleQuery: {
account: {
profile: {
get: {
queryKey: () => [['console', 'account', 'profile', 'get'], { type: 'query' }],
},
},
},
systemFeatures: {
get: {
queryKey: () => ['console', 'systemFeatures'],
@@ -364,10 +371,6 @@ vi.mock('@/service/client', () => ({
},
}))
vi.mock('@/context/account-state', async () => {
const { createAccountStateModuleMock } = await import('@/test/console/state-fixture')
return createAccountStateModuleMock(() => mockConsoleState)
})
vi.mock('@/context/workspace-state', async () => {
const { createWorkspaceStateModuleMock } = await import('@/test/console/state-fixture')
return createWorkspaceStateModuleMock(() => mockConsoleState)
@@ -551,6 +554,7 @@ const renderHomeContent = ({
}: RenderOptions = {}) => {
mockAppCreatePermission(hasEditPermission)
const { wrapper: ConsoleQueryWrapper, queryClient } = createConsoleQueryWrapper({
accountProfile: mockConsoleState.userProfile,
systemFeatures: {
deployment_edition: options.deploymentEdition ?? 'COMMUNITY',
enable_explore_banner: options.enableExploreBanner ?? false,
+7 -8
View File
@@ -26,14 +26,6 @@ vi.mock('@/app/components/workflow/plugin-dependency/hooks', () => ({
}),
}))
vi.mock('@/context/account-state', async () => {
const { createAccountStateModuleMock } = await import('@/test/console/state-fixture')
return createAccountStateModuleMock(() => ({
userProfile: { id: 'user-1' },
}))
})
vi.mock('@/context/permission-state', async () => {
const { createPermissionStateModuleMock } = await import('@/test/console/state-fixture')
@@ -53,6 +45,13 @@ vi.mock('@/service/client', async (importOriginal) => {
...actual,
consoleQuery: {
...actual.consoleQuery,
account: {
profile: {
get: {
queryKey: () => [['console', 'account', 'profile', 'get'], { type: 'query' }],
},
},
},
systemFeatures: actual.consoleQuery.systemFeatures,
apps: {
...actual.consoleQuery.apps,
+5 -2
View File
@@ -6,8 +6,8 @@ import { useAtomValue } from 'jotai'
import { useCallback, useRef, useState } from 'react'
import { useTranslation } from 'react-i18next'
import { usePluginDependencies } from '@/app/components/workflow/plugin-dependency/hooks'
import { userProfileIdAtom } from '@/context/account-state'
import { workspacePermissionKeysAtom } from '@/context/permission-state'
import { userProfileQueryOptions } from '@/features/account-profile/client'
import { systemFeaturesQueryOptions } from '@/features/system-features/client'
import { DSLImportStatus } from '@/models/app'
import { useRouter } from '@/next/navigation'
@@ -35,7 +35,10 @@ export const useImportDSL = () => {
const actionInFlightRef = useRef(false)
const [isFetching, setIsFetching] = useState(false)
const { data: systemFeatures } = useSuspenseQuery(systemFeaturesQueryOptions())
const currentUserId = useAtomValue(userProfileIdAtom)
const { data: currentUserId } = useSuspenseQuery({
...userProfileQueryOptions(),
select: (data) => data.profile.id,
})
const workspacePermissionKeys = useAtomValue(workspacePermissionKeysAtom)
const isRbacEnabled = systemFeatures.rbac_enabled
const [versions, setVersions] = useState<{ importedVersion: string; systemVersion: string }>()