mirror of
https://github.com/langgenius/dify.git
synced 2026-09-08 11:04:27 +08:00
refactor(web): migrate plugins and tools app context consumers (#38533)
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
This commit is contained in:
parent
98f67e9c82
commit
503d80be1d
@ -3457,14 +3457,6 @@
|
|||||||
"count": 2
|
"count": 2
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"web/app/components/header/account-setting/model-provider-page/model-auth/__tests__/switch-credential-in-load-balancing.spec.tsx": {
|
|
||||||
"jsx-a11y/click-events-have-key-events": {
|
|
||||||
"count": 1
|
|
||||||
},
|
|
||||||
"jsx-a11y/no-static-element-interactions": {
|
|
||||||
"count": 1
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"web/app/components/header/account-setting/model-provider-page/model-auth/add-custom-model.tsx": {
|
"web/app/components/header/account-setting/model-provider-page/model-auth/add-custom-model.tsx": {
|
||||||
"jsx-a11y/click-events-have-key-events": {
|
"jsx-a11y/click-events-have-key-events": {
|
||||||
"count": 2
|
"count": 2
|
||||||
|
|||||||
@ -44,7 +44,13 @@ vi.mock('@/context/app-context', () => ({
|
|||||||
isCurrentWorkspaceManager: false,
|
isCurrentWorkspaceManager: false,
|
||||||
isCurrentWorkspaceOwner: false,
|
isCurrentWorkspaceOwner: false,
|
||||||
langGeniusVersionInfo: {
|
langGeniusVersionInfo: {
|
||||||
|
current_env: 'CLOUD',
|
||||||
current_version: '1.0.0',
|
current_version: '1.0.0',
|
||||||
|
latest_version: '1.0.0',
|
||||||
|
version: '1.0.0',
|
||||||
|
release_date: '',
|
||||||
|
release_notes: '',
|
||||||
|
can_auto_update: false,
|
||||||
},
|
},
|
||||||
workspacePermissionKeys: [
|
workspacePermissionKeys: [
|
||||||
'plugin.install',
|
'plugin.install',
|
||||||
@ -54,6 +60,33 @@ vi.mock('@/context/app-context', () => ({
|
|||||||
}),
|
}),
|
||||||
}))
|
}))
|
||||||
|
|
||||||
|
vi.mock('@/context/app-context-state', async (importOriginal) => {
|
||||||
|
const { createAppContextStateAtomMock } = await import('@/__tests__/utils/mock-app-context-state')
|
||||||
|
return createAppContextStateAtomMock(importOriginal, () => ({
|
||||||
|
isCurrentWorkspaceManager: false,
|
||||||
|
isCurrentWorkspaceOwner: false,
|
||||||
|
langGeniusVersionInfo: {
|
||||||
|
current_env: 'CLOUD',
|
||||||
|
current_version: '1.0.0',
|
||||||
|
latest_version: '1.0.0',
|
||||||
|
version: '1.0.0',
|
||||||
|
release_date: '',
|
||||||
|
release_notes: '',
|
||||||
|
can_auto_update: false,
|
||||||
|
},
|
||||||
|
workspacePermissionKeys: [
|
||||||
|
'plugin.install',
|
||||||
|
'plugin.delete',
|
||||||
|
'plugin.plugin_preferences',
|
||||||
|
],
|
||||||
|
}))
|
||||||
|
})
|
||||||
|
|
||||||
|
vi.mock('jotai', async (importOriginal) => {
|
||||||
|
const { createAppContextStateJotaiMock } = await import('@/__tests__/utils/mock-app-context-state')
|
||||||
|
return createAppContextStateJotaiMock(importOriginal)
|
||||||
|
})
|
||||||
|
|
||||||
vi.mock('@/service/use-plugins', () => ({
|
vi.mock('@/service/use-plugins', () => ({
|
||||||
hasPluginPermission: () => true,
|
hasPluginPermission: () => true,
|
||||||
useReferenceSettings: () => ({
|
useReferenceSettings: () => ({
|
||||||
|
|||||||
@ -53,6 +53,19 @@ vi.mock('@/context/app-context', () => ({
|
|||||||
}),
|
}),
|
||||||
}))
|
}))
|
||||||
|
|
||||||
|
vi.mock('@/context/app-context-state', async (importOriginal) => {
|
||||||
|
const { createAppContextStateAtomMock } = await import('@/__tests__/utils/mock-app-context-state')
|
||||||
|
return createAppContextStateAtomMock(importOriginal, () => ({
|
||||||
|
isCurrentWorkspaceManager: true,
|
||||||
|
workspacePermissionKeys: ['tool.manage', 'credential.create', 'credential.manage', 'credential.use'],
|
||||||
|
}))
|
||||||
|
})
|
||||||
|
|
||||||
|
vi.mock('jotai', async (importOriginal) => {
|
||||||
|
const { createAppContextStateJotaiMock } = await import('@/__tests__/utils/mock-app-context-state')
|
||||||
|
return createAppContextStateJotaiMock(importOriginal)
|
||||||
|
})
|
||||||
|
|
||||||
const mockSetShowModelModal = vi.fn()
|
const mockSetShowModelModal = vi.fn()
|
||||||
vi.mock('@/context/modal-context', () => ({
|
vi.mock('@/context/modal-context', () => ({
|
||||||
useModalContext: () => ({
|
useModalContext: () => ({
|
||||||
|
|||||||
@ -14,6 +14,10 @@ export type AppContextStateMockState = {
|
|||||||
currentWorkspace?: {
|
currentWorkspace?: {
|
||||||
id?: string
|
id?: string
|
||||||
} | null
|
} | null
|
||||||
|
isCurrentWorkspaceManager?: boolean
|
||||||
|
isCurrentWorkspaceOwner?: boolean
|
||||||
|
isCurrentWorkspaceEditor?: boolean
|
||||||
|
isCurrentWorkspaceDatasetOperator?: boolean
|
||||||
isLoadingCurrentWorkspace?: boolean
|
isLoadingCurrentWorkspace?: boolean
|
||||||
isLoadingWorkspacePermissionKeys?: boolean
|
isLoadingWorkspacePermissionKeys?: boolean
|
||||||
workspacePermissionKeys?: string[]
|
workspacePermissionKeys?: string[]
|
||||||
@ -25,6 +29,7 @@ type AppContextStateAtomKind
|
|||||||
| 'userProfileId'
|
| 'userProfileId'
|
||||||
| 'currentWorkspace'
|
| 'currentWorkspace'
|
||||||
| 'currentWorkspaceId'
|
| 'currentWorkspaceId'
|
||||||
|
| 'workspaceRoleFlags'
|
||||||
| 'currentWorkspaceLoading'
|
| 'currentWorkspaceLoading'
|
||||||
| 'workspacePermissionKeys'
|
| 'workspacePermissionKeys'
|
||||||
| 'workspacePermissionKeysLoading'
|
| 'workspacePermissionKeysLoading'
|
||||||
@ -98,6 +103,7 @@ export const createAppContextStateAtomMock = async (
|
|||||||
userProfileIdAtom: createMockAtom('userProfileId'),
|
userProfileIdAtom: createMockAtom('userProfileId'),
|
||||||
currentWorkspaceAtom: createMockAtom('currentWorkspace'),
|
currentWorkspaceAtom: createMockAtom('currentWorkspace'),
|
||||||
currentWorkspaceIdAtom: createMockAtom('currentWorkspaceId'),
|
currentWorkspaceIdAtom: createMockAtom('currentWorkspaceId'),
|
||||||
|
workspaceRoleFlagsAtom: createMockAtom('workspaceRoleFlags'),
|
||||||
currentWorkspaceLoadingAtom: createMockAtom('currentWorkspaceLoading'),
|
currentWorkspaceLoadingAtom: createMockAtom('currentWorkspaceLoading'),
|
||||||
workspacePermissionKeysAtom: createMockAtom('workspacePermissionKeys'),
|
workspacePermissionKeysAtom: createMockAtom('workspacePermissionKeys'),
|
||||||
workspacePermissionKeysLoadingAtom: createMockAtom('workspacePermissionKeysLoading'),
|
workspacePermissionKeysLoadingAtom: createMockAtom('workspacePermissionKeysLoading'),
|
||||||
@ -135,6 +141,15 @@ export const createAppContextStateJotaiMock = async (
|
|||||||
if (atom[APP_CONTEXT_STATE_ATOM_KIND] === 'currentWorkspaceId')
|
if (atom[APP_CONTEXT_STATE_ATOM_KIND] === 'currentWorkspaceId')
|
||||||
return currentWorkspace.id
|
return currentWorkspace.id
|
||||||
|
|
||||||
|
if (atom[APP_CONTEXT_STATE_ATOM_KIND] === 'workspaceRoleFlags') {
|
||||||
|
return {
|
||||||
|
isCurrentWorkspaceManager: state.isCurrentWorkspaceManager ?? false,
|
||||||
|
isCurrentWorkspaceOwner: state.isCurrentWorkspaceOwner ?? false,
|
||||||
|
isCurrentWorkspaceEditor: state.isCurrentWorkspaceEditor ?? false,
|
||||||
|
isCurrentWorkspaceDatasetOperator: state.isCurrentWorkspaceDatasetOperator ?? false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (atom[APP_CONTEXT_STATE_ATOM_KIND] === 'currentWorkspaceLoading')
|
if (atom[APP_CONTEXT_STATE_ATOM_KIND] === 'currentWorkspaceLoading')
|
||||||
return state.isLoadingCurrentWorkspace ?? false
|
return state.isLoadingCurrentWorkspace ?? false
|
||||||
|
|
||||||
|
|||||||
@ -116,6 +116,19 @@ vi.mock('@/context/app-context', () => ({
|
|||||||
}),
|
}),
|
||||||
}))
|
}))
|
||||||
|
|
||||||
|
vi.mock('@/context/app-context-state', async (importOriginal) => {
|
||||||
|
const { createAppContextStateAtomMock } = await import('@/__tests__/utils/mock-app-context-state')
|
||||||
|
return createAppContextStateAtomMock(importOriginal, () => ({
|
||||||
|
isCurrentWorkspaceManager: true,
|
||||||
|
workspacePermissionKeys: mockWorkspacePermissionKeys,
|
||||||
|
}))
|
||||||
|
})
|
||||||
|
|
||||||
|
vi.mock('jotai', async (importOriginal) => {
|
||||||
|
const { createAppContextStateJotaiMock } = await import('@/__tests__/utils/mock-app-context-state')
|
||||||
|
return createAppContextStateJotaiMock(importOriginal)
|
||||||
|
})
|
||||||
|
|
||||||
vi.mock('@langgenius/dify-ui/toast', () => ({
|
vi.mock('@langgenius/dify-ui/toast', () => ({
|
||||||
toast: {
|
toast: {
|
||||||
error: (...args: unknown[]) => mockToastError(...args),
|
error: (...args: unknown[]) => mockToastError(...args),
|
||||||
|
|||||||
@ -31,6 +31,16 @@ vi.mock('@/context/app-context', async (importOriginal) => {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
vi.mock('@/context/app-context-state', async (importOriginal) => {
|
||||||
|
const { createAppContextStateAtomMock } = await import('@/__tests__/utils/mock-app-context-state')
|
||||||
|
return createAppContextStateAtomMock(importOriginal, () => mockAppContextState.current ?? {})
|
||||||
|
})
|
||||||
|
|
||||||
|
vi.mock('jotai', async (importOriginal) => {
|
||||||
|
const { createAppContextStateJotaiMock } = await import('@/__tests__/utils/mock-app-context-state')
|
||||||
|
return createAppContextStateJotaiMock(importOriginal)
|
||||||
|
})
|
||||||
|
|
||||||
vi.mock('@/next/navigation', () => ({
|
vi.mock('@/next/navigation', () => ({
|
||||||
useRouter: vi.fn(() => ({
|
useRouter: vi.fn(() => ({
|
||||||
push: vi.fn(),
|
push: vi.fn(),
|
||||||
@ -77,10 +87,14 @@ vi.mock('@/service/use-datasource', () => ({
|
|||||||
useGetDataSourceListAuth: vi.fn(() => ({ data: { result: [] } })),
|
useGetDataSourceListAuth: vi.fn(() => ({ data: { result: [] } })),
|
||||||
}))
|
}))
|
||||||
|
|
||||||
vi.mock('@/service/use-common', () => ({
|
vi.mock('@/service/use-common', async (importOriginal) => {
|
||||||
useMembers: vi.fn(() => ({ data: { accounts: [] }, refetch: vi.fn() })),
|
const actual = await importOriginal<typeof import('@/service/use-common')>()
|
||||||
useProviderContext: vi.fn(),
|
return {
|
||||||
}))
|
...actual,
|
||||||
|
useMembers: vi.fn(() => ({ data: { accounts: [] }, refetch: vi.fn() })),
|
||||||
|
useProviderContext: vi.fn(),
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
vi.mock('@/service/client', async (importOriginal) => {
|
vi.mock('@/service/client', async (importOriginal) => {
|
||||||
const actual = await importOriginal<typeof import('@/service/client')>()
|
const actual = await importOriginal<typeof import('@/service/client')>()
|
||||||
|
|||||||
@ -19,6 +19,18 @@ vi.mock('@/context/app-context', () => ({
|
|||||||
}),
|
}),
|
||||||
}))
|
}))
|
||||||
|
|
||||||
|
vi.mock('@/context/app-context-state', async (importOriginal) => {
|
||||||
|
const { createAppContextStateAtomMock } = await import('@/__tests__/utils/mock-app-context-state')
|
||||||
|
return createAppContextStateAtomMock(importOriginal, () => ({
|
||||||
|
workspacePermissionKeys: mockWorkspacePermissionKeys,
|
||||||
|
}))
|
||||||
|
})
|
||||||
|
|
||||||
|
vi.mock('jotai', async (importOriginal) => {
|
||||||
|
const { createAppContextStateJotaiMock } = await import('@/__tests__/utils/mock-app-context-state')
|
||||||
|
return createAppContextStateJotaiMock(importOriginal)
|
||||||
|
})
|
||||||
|
|
||||||
vi.mock('@/app/components/plugins/plugin-auth', () => ({
|
vi.mock('@/app/components/plugins/plugin-auth', () => ({
|
||||||
ApiKeyModal: vi.fn(({ onClose, onUpdate, onRemove, disabled, editValues }: { onClose: () => void, onUpdate: () => void, onRemove: () => void, disabled: boolean, editValues: Record<string, unknown> }) => (
|
ApiKeyModal: vi.fn(({ onClose, onUpdate, onRemove, disabled, editValues }: { onClose: () => void, onUpdate: () => void, onRemove: () => void, disabled: boolean, editValues: Record<string, unknown> }) => (
|
||||||
<div data-testid="mock-api-key-modal" data-disabled={disabled}>
|
<div data-testid="mock-api-key-modal" data-disabled={disabled}>
|
||||||
|
|||||||
@ -13,6 +13,18 @@ vi.mock('@/context/app-context', () => ({
|
|||||||
}),
|
}),
|
||||||
}))
|
}))
|
||||||
|
|
||||||
|
vi.mock('@/context/app-context-state', async (importOriginal) => {
|
||||||
|
const { createAppContextStateAtomMock } = await import('@/__tests__/utils/mock-app-context-state')
|
||||||
|
return createAppContextStateAtomMock(importOriginal, () => ({
|
||||||
|
workspacePermissionKeys: mockWorkspacePermissionKeys.value,
|
||||||
|
}))
|
||||||
|
})
|
||||||
|
|
||||||
|
vi.mock('jotai', async (importOriginal) => {
|
||||||
|
const { createAppContextStateJotaiMock } = await import('@/__tests__/utils/mock-app-context-state')
|
||||||
|
return createAppContextStateJotaiMock(importOriginal)
|
||||||
|
})
|
||||||
|
|
||||||
vi.mock('@/app/components/header/account-setting/model-provider-page/model-auth', () => ({
|
vi.mock('@/app/components/header/account-setting/model-provider-page/model-auth', () => ({
|
||||||
Authorized: ({
|
Authorized: ({
|
||||||
renderTrigger,
|
renderTrigger,
|
||||||
|
|||||||
@ -34,6 +34,18 @@ vi.mock('@/context/app-context', () => ({
|
|||||||
}),
|
}),
|
||||||
}))
|
}))
|
||||||
|
|
||||||
|
vi.mock('@/context/app-context-state', async (importOriginal) => {
|
||||||
|
const { createAppContextStateAtomMock } = await import('@/__tests__/utils/mock-app-context-state')
|
||||||
|
return createAppContextStateAtomMock(importOriginal, () => ({
|
||||||
|
workspacePermissionKeys: mockWorkspacePermissionKeys.value,
|
||||||
|
}))
|
||||||
|
})
|
||||||
|
|
||||||
|
vi.mock('jotai', async (importOriginal) => {
|
||||||
|
const { createAppContextStateJotaiMock } = await import('@/__tests__/utils/mock-app-context-state')
|
||||||
|
return createAppContextStateJotaiMock(importOriginal)
|
||||||
|
})
|
||||||
|
|
||||||
// Mock components
|
// Mock components
|
||||||
vi.mock('../../model-icon', () => ({
|
vi.mock('../../model-icon', () => ({
|
||||||
default: () => <div data-testid="model-icon" />,
|
default: () => <div data-testid="model-icon" />,
|
||||||
|
|||||||
@ -14,6 +14,18 @@ vi.mock('@/context/app-context', () => ({
|
|||||||
}),
|
}),
|
||||||
}))
|
}))
|
||||||
|
|
||||||
|
vi.mock('@/context/app-context-state', async (importOriginal) => {
|
||||||
|
const { createAppContextStateAtomMock } = await import('@/__tests__/utils/mock-app-context-state')
|
||||||
|
return createAppContextStateAtomMock(importOriginal, () => ({
|
||||||
|
workspacePermissionKeys: mockWorkspacePermissionKeys.value,
|
||||||
|
}))
|
||||||
|
})
|
||||||
|
|
||||||
|
vi.mock('jotai', async (importOriginal) => {
|
||||||
|
const { createAppContextStateJotaiMock } = await import('@/__tests__/utils/mock-app-context-state')
|
||||||
|
return createAppContextStateJotaiMock(importOriginal)
|
||||||
|
})
|
||||||
|
|
||||||
// Mock components
|
// Mock components
|
||||||
vi.mock('../authorized', () => ({
|
vi.mock('../authorized', () => ({
|
||||||
default: ({
|
default: ({
|
||||||
@ -37,7 +49,12 @@ vi.mock('../authorized', () => ({
|
|||||||
data-hide-add-action={String(!!hideAddAction)}
|
data-hide-add-action={String(!!hideAddAction)}
|
||||||
data-trigger-only-open-modal={String(!!triggerOnlyOpenModal)}
|
data-trigger-only-open-modal={String(!!triggerOnlyOpenModal)}
|
||||||
>
|
>
|
||||||
<div data-testid="trigger-container" onClick={() => onItemClick?.(items[0]!.credentials[0])}>
|
<div
|
||||||
|
role="presentation"
|
||||||
|
data-testid="trigger-container"
|
||||||
|
onClick={() => onItemClick?.(items[0]!.credentials[0])}
|
||||||
|
onKeyDown={() => undefined}
|
||||||
|
>
|
||||||
{renderTrigger()}
|
{renderTrigger()}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -19,6 +19,18 @@ vi.mock('@/context/app-context', () => ({
|
|||||||
}),
|
}),
|
||||||
}))
|
}))
|
||||||
|
|
||||||
|
vi.mock('@/context/app-context-state', async (importOriginal) => {
|
||||||
|
const { createAppContextStateAtomMock } = await import('@/__tests__/utils/mock-app-context-state')
|
||||||
|
return createAppContextStateAtomMock(importOriginal, () => ({
|
||||||
|
workspacePermissionKeys: mockWorkspacePermissionKeys,
|
||||||
|
}))
|
||||||
|
})
|
||||||
|
|
||||||
|
vi.mock('jotai', async (importOriginal) => {
|
||||||
|
const { createAppContextStateJotaiMock } = await import('@/__tests__/utils/mock-app-context-state')
|
||||||
|
return createAppContextStateJotaiMock(importOriginal)
|
||||||
|
})
|
||||||
|
|
||||||
vi.mock('../../hooks', () => ({
|
vi.mock('../../hooks', () => ({
|
||||||
useAuth: () => ({
|
useAuth: () => ({
|
||||||
openConfirmDelete: mockOpenConfirmDelete,
|
openConfirmDelete: mockOpenConfirmDelete,
|
||||||
|
|||||||
@ -80,6 +80,18 @@ vi.mock('@/context/app-context', () => ({
|
|||||||
selector({ workspacePermissionKeys: mockState.workspacePermissionKeys }),
|
selector({ workspacePermissionKeys: mockState.workspacePermissionKeys }),
|
||||||
}))
|
}))
|
||||||
|
|
||||||
|
vi.mock('@/context/app-context-state', async (importOriginal) => {
|
||||||
|
const { createAppContextStateAtomMock } = await import('@/__tests__/utils/mock-app-context-state')
|
||||||
|
return createAppContextStateAtomMock(importOriginal, () => ({
|
||||||
|
workspacePermissionKeys: mockState.workspacePermissionKeys,
|
||||||
|
}))
|
||||||
|
})
|
||||||
|
|
||||||
|
vi.mock('jotai', async (importOriginal) => {
|
||||||
|
const { createAppContextStateJotaiMock } = await import('@/__tests__/utils/mock-app-context-state')
|
||||||
|
return createAppContextStateJotaiMock(importOriginal)
|
||||||
|
})
|
||||||
|
|
||||||
vi.mock('@/hooks/use-i18n', () => ({
|
vi.mock('@/hooks/use-i18n', () => ({
|
||||||
useRenderI18nObject: () => (value: { en_US: string }) => value.en_US,
|
useRenderI18nObject: () => (value: { en_US: string }) => value.en_US,
|
||||||
}))
|
}))
|
||||||
|
|||||||
@ -94,6 +94,18 @@ vi.mock('@/context/app-context', () => ({
|
|||||||
}),
|
}),
|
||||||
}))
|
}))
|
||||||
|
|
||||||
|
vi.mock('@/context/app-context-state', async (importOriginal) => {
|
||||||
|
const { createAppContextStateAtomMock } = await import('@/__tests__/utils/mock-app-context-state')
|
||||||
|
return createAppContextStateAtomMock(importOriginal, () => ({
|
||||||
|
workspacePermissionKeys: mockWorkspacePermissionKeys.value,
|
||||||
|
}))
|
||||||
|
})
|
||||||
|
|
||||||
|
vi.mock('jotai', async (importOriginal) => {
|
||||||
|
const { createAppContextStateJotaiMock } = await import('@/__tests__/utils/mock-app-context-state')
|
||||||
|
return createAppContextStateJotaiMock(importOriginal)
|
||||||
|
})
|
||||||
|
|
||||||
const makeModelItem = (overrides: Partial<ModelItem> = {}): ModelItem => ({
|
const makeModelItem = (overrides: Partial<ModelItem> = {}): ModelItem => ({
|
||||||
model: 'gpt-4',
|
model: 'gpt-4',
|
||||||
label: { en_US: 'GPT-4', zh_Hans: 'GPT-4' },
|
label: { en_US: 'GPT-4', zh_Hans: 'GPT-4' },
|
||||||
|
|||||||
@ -43,6 +43,19 @@ vi.mock('@/context/app-context', () => ({
|
|||||||
}),
|
}),
|
||||||
}))
|
}))
|
||||||
|
|
||||||
|
vi.mock('@/context/app-context-state', async (importOriginal) => {
|
||||||
|
const { createAppContextStateAtomMock } = await import('@/__tests__/utils/mock-app-context-state')
|
||||||
|
return createAppContextStateAtomMock(importOriginal, () => ({
|
||||||
|
isCurrentWorkspaceManager: mockIsCurrentWorkspaceManager,
|
||||||
|
workspacePermissionKeys: mockWorkspacePermissionKeys,
|
||||||
|
}))
|
||||||
|
})
|
||||||
|
|
||||||
|
vi.mock('jotai', async (importOriginal) => {
|
||||||
|
const { createAppContextStateJotaiMock } = await import('@/__tests__/utils/mock-app-context-state')
|
||||||
|
return createAppContextStateJotaiMock(importOriginal)
|
||||||
|
})
|
||||||
|
|
||||||
// Mock internal components to simplify testing of the index file
|
// Mock internal components to simplify testing of the index file
|
||||||
vi.mock('../credential-panel', () => ({
|
vi.mock('../credential-panel', () => ({
|
||||||
default: () => <div data-testid="credential-panel" />,
|
default: () => <div data-testid="credential-panel" />,
|
||||||
|
|||||||
@ -9,6 +9,18 @@ vi.mock('@/context/app-context', () => ({
|
|||||||
}),
|
}),
|
||||||
}))
|
}))
|
||||||
|
|
||||||
|
vi.mock('@/context/app-context-state', async (importOriginal) => {
|
||||||
|
const { createAppContextStateAtomMock } = await import('@/__tests__/utils/mock-app-context-state')
|
||||||
|
return createAppContextStateAtomMock(importOriginal, () => ({
|
||||||
|
workspacePermissionKeys: ['credential.use', 'credential.create', 'credential.manage'],
|
||||||
|
}))
|
||||||
|
})
|
||||||
|
|
||||||
|
vi.mock('jotai', async (importOriginal) => {
|
||||||
|
const { createAppContextStateJotaiMock } = await import('@/__tests__/utils/mock-app-context-state')
|
||||||
|
return createAppContextStateJotaiMock(importOriginal)
|
||||||
|
})
|
||||||
|
|
||||||
const createCredential = (overrides: Partial<Credential> = {}): Credential => ({
|
const createCredential = (overrides: Partial<Credential> = {}): Credential => ({
|
||||||
credential_id: 'cred-1',
|
credential_id: 'cred-1',
|
||||||
credential_name: 'Test API Key',
|
credential_name: 'Test API Key',
|
||||||
|
|||||||
@ -41,6 +41,18 @@ vi.mock('@/context/app-context', () => ({
|
|||||||
}),
|
}),
|
||||||
}))
|
}))
|
||||||
|
|
||||||
|
vi.mock('@/context/app-context-state', async (importOriginal) => {
|
||||||
|
const { createAppContextStateAtomMock } = await import('@/__tests__/utils/mock-app-context-state')
|
||||||
|
return createAppContextStateAtomMock(importOriginal, () => ({
|
||||||
|
workspacePermissionKeys: mockWorkspacePermissionKeys,
|
||||||
|
}))
|
||||||
|
})
|
||||||
|
|
||||||
|
vi.mock('jotai', async (importOriginal) => {
|
||||||
|
const { createAppContextStateJotaiMock } = await import('@/__tests__/utils/mock-app-context-state')
|
||||||
|
return createAppContextStateJotaiMock(importOriginal)
|
||||||
|
})
|
||||||
|
|
||||||
vi.mock('../../../model-auth/authorized/credential-item', () => ({
|
vi.mock('../../../model-auth/authorized/credential-item', () => ({
|
||||||
default: ({ credential, disabled, disableEdit, disableDelete, onItemClick, onEdit, onDelete }: {
|
default: ({ credential, disabled, disableEdit, disableDelete, onItemClick, onEdit, onDelete }: {
|
||||||
credential: { credential_id: string, credential_name: string }
|
credential: { credential_id: string, credential_name: string }
|
||||||
|
|||||||
@ -103,6 +103,18 @@ vi.mock('@/context/app-context', () => ({
|
|||||||
}),
|
}),
|
||||||
}))
|
}))
|
||||||
|
|
||||||
|
vi.mock('@/context/app-context-state', async (importOriginal) => {
|
||||||
|
const { createAppContextStateAtomMock } = await import('@/__tests__/utils/mock-app-context-state')
|
||||||
|
return createAppContextStateAtomMock(importOriginal, () => ({
|
||||||
|
workspacePermissionKeys: mockAppContextState.workspacePermissionKeys,
|
||||||
|
}))
|
||||||
|
})
|
||||||
|
|
||||||
|
vi.mock('jotai', async (importOriginal) => {
|
||||||
|
const { createAppContextStateJotaiMock } = await import('@/__tests__/utils/mock-app-context-state')
|
||||||
|
return createAppContextStateJotaiMock(importOriginal)
|
||||||
|
})
|
||||||
|
|
||||||
let mockCheckedInstalledData: { plugins: { id: string, name: string }[] } | null = null
|
let mockCheckedInstalledData: { plugins: { id: string, name: string }[] } | null = null
|
||||||
const mockInvalidateInstalledPluginList = vi.fn()
|
const mockInvalidateInstalledPluginList = vi.fn()
|
||||||
vi.mock('@/service/use-plugins', () => ({
|
vi.mock('@/service/use-plugins', () => ({
|
||||||
|
|||||||
@ -41,11 +41,17 @@ vi.mock('@/utils/format', () => ({
|
|||||||
formatNumber: (num: number) => num.toLocaleString(),
|
formatNumber: (num: number) => num.toLocaleString(),
|
||||||
}))
|
}))
|
||||||
|
|
||||||
vi.mock('@/context/app-context', () => ({
|
vi.mock('@/context/app-context-state', async (importOriginal) => {
|
||||||
useSelector: (selector: (value: { currentWorkspace: { id: string } }) => string) => selector({
|
const { createAppContextStateAtomMock } = await import('@/__tests__/utils/mock-app-context-state')
|
||||||
|
return createAppContextStateAtomMock(importOriginal, () => ({
|
||||||
currentWorkspace: { id: 'workspace-123' },
|
currentWorkspace: { id: 'workspace-123' },
|
||||||
}),
|
}))
|
||||||
}))
|
})
|
||||||
|
|
||||||
|
vi.mock('jotai', async (importOriginal) => {
|
||||||
|
const { createAppContextStateJotaiMock } = await import('@/__tests__/utils/mock-app-context-state')
|
||||||
|
return createAppContextStateJotaiMock(importOriginal)
|
||||||
|
})
|
||||||
|
|
||||||
vi.mock('@/utils/mcp', () => ({
|
vi.mock('@/utils/mcp', () => ({
|
||||||
shouldUseMcpIcon: (src: unknown) => typeof src === 'object' && src !== null && (src as { content?: string })?.content === '🔗',
|
shouldUseMcpIcon: (src: unknown) => typeof src === 'object' && src !== null && (src as { content?: string })?.content === '🔗',
|
||||||
|
|||||||
@ -1,9 +1,10 @@
|
|||||||
'use client'
|
'use client'
|
||||||
import type { Plugin } from '../types'
|
import type { Plugin } from '../types'
|
||||||
import { cn } from '@langgenius/dify-ui/cn'
|
import { cn } from '@langgenius/dify-ui/cn'
|
||||||
|
import { useAtomValue } from 'jotai'
|
||||||
import * as React from 'react'
|
import * as React from 'react'
|
||||||
import { useTranslation } from '#i18n'
|
import { useTranslation } from '#i18n'
|
||||||
import { useSelector } from '@/context/app-context'
|
import { currentWorkspaceIdAtom } from '@/context/app-context-state'
|
||||||
import { useGetLanguage } from '@/context/i18n'
|
import { useGetLanguage } from '@/context/i18n'
|
||||||
import useTheme from '@/hooks/use-theme'
|
import useTheme from '@/hooks/use-theme'
|
||||||
import {
|
import {
|
||||||
@ -61,7 +62,7 @@ const Card = ({
|
|||||||
const locale = useGetLanguage()
|
const locale = useGetLanguage()
|
||||||
const { t } = useTranslation()
|
const { t } = useTranslation()
|
||||||
const { categoriesMap } = useCategories(true)
|
const { categoriesMap } = useCategories(true)
|
||||||
const currentWorkspaceId = useSelector(s => s.currentWorkspace.id)
|
const currentWorkspaceId = useAtomValue(currentWorkspaceIdAtom)
|
||||||
const { category, type, name, org, label, brief, icon, icon_dark, verified, from } = payload
|
const { category, type, name, org, label, brief, icon, icon_dark, verified, from } = payload
|
||||||
const badges = payload.badges ?? []
|
const badges = payload.badges ?? []
|
||||||
const { theme } = useTheme()
|
const { theme } = useTheme()
|
||||||
|
|||||||
@ -2,15 +2,27 @@ import { renderHook } from '@testing-library/react'
|
|||||||
import { describe, expect, it, vi } from 'vitest'
|
import { describe, expect, it, vi } from 'vitest'
|
||||||
import useGetIcon from '../use-get-icon'
|
import useGetIcon from '../use-get-icon'
|
||||||
|
|
||||||
|
const mockCurrentWorkspaceIdAtom = vi.hoisted(() => Symbol('currentWorkspaceIdAtom'))
|
||||||
|
|
||||||
vi.mock('@/config', () => ({
|
vi.mock('@/config', () => ({
|
||||||
API_PREFIX: 'https://api.example.com',
|
API_PREFIX: 'https://api.example.com',
|
||||||
}))
|
}))
|
||||||
|
|
||||||
vi.mock('@/context/app-context', () => ({
|
vi.mock('@/context/app-context-state', () => ({
|
||||||
useSelector: (selector: (state: { currentWorkspace: { id: string } }) => string | { id: string }) =>
|
currentWorkspaceIdAtom: mockCurrentWorkspaceIdAtom,
|
||||||
selector({ currentWorkspace: { id: 'workspace-123' } }),
|
|
||||||
}))
|
}))
|
||||||
|
|
||||||
|
vi.mock('jotai', () => {
|
||||||
|
return {
|
||||||
|
useAtomValue: (atom: unknown) => {
|
||||||
|
if (atom === mockCurrentWorkspaceIdAtom)
|
||||||
|
return 'workspace-123'
|
||||||
|
|
||||||
|
throw new Error('Unexpected atom')
|
||||||
|
},
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
describe('useGetIcon', () => {
|
describe('useGetIcon', () => {
|
||||||
it('builds icon url with current workspace id', () => {
|
it('builds icon url with current workspace id', () => {
|
||||||
const { result } = renderHook(() => useGetIcon())
|
const { result } = renderHook(() => useGetIcon())
|
||||||
|
|||||||
@ -1,12 +1,13 @@
|
|||||||
|
import { useAtomValue } from 'jotai'
|
||||||
import { useCallback } from 'react'
|
import { useCallback } from 'react'
|
||||||
import { API_PREFIX } from '@/config'
|
import { API_PREFIX } from '@/config'
|
||||||
import { useSelector } from '@/context/app-context'
|
import { currentWorkspaceIdAtom } from '@/context/app-context-state'
|
||||||
|
|
||||||
const useGetIcon = () => {
|
const useGetIcon = () => {
|
||||||
const currentWorkspace = useSelector(s => s.currentWorkspace)
|
const currentWorkspaceId = useAtomValue(currentWorkspaceIdAtom)
|
||||||
const getIconUrl = useCallback((fileName: string) => {
|
const getIconUrl = useCallback((fileName: string) => {
|
||||||
return `${API_PREFIX}/workspaces/current/plugin/icon?tenant_id=${currentWorkspace.id}&filename=${fileName}`
|
return `${API_PREFIX}/workspaces/current/plugin/icon?tenant_id=${currentWorkspaceId}&filename=${fileName}`
|
||||||
}, [currentWorkspace.id])
|
}, [currentWorkspaceId])
|
||||||
|
|
||||||
return {
|
return {
|
||||||
getIconUrl,
|
getIconUrl,
|
||||||
|
|||||||
@ -4,12 +4,26 @@ import useWorkspacePluginInstallPermission from '../use-workspace-plugin-install
|
|||||||
|
|
||||||
let mockWorkspacePermissionKeys: string[] = []
|
let mockWorkspacePermissionKeys: string[] = []
|
||||||
|
|
||||||
vi.mock('@/context/app-context', () => ({
|
vi.mock('@/context/app-context-state', async (importOriginal) => {
|
||||||
useAppContext: () => ({
|
const { createAppContextStateAtomMock } = await import('@/__tests__/utils/mock-app-context-state')
|
||||||
langGeniusVersionInfo: { current_version: '1.0.0' },
|
return createAppContextStateAtomMock(importOriginal, () => ({
|
||||||
|
langGeniusVersionInfo: {
|
||||||
|
current_env: '',
|
||||||
|
current_version: '1.0.0',
|
||||||
|
latest_version: '',
|
||||||
|
release_date: '',
|
||||||
|
release_notes: '',
|
||||||
|
version: '',
|
||||||
|
can_auto_update: false,
|
||||||
|
},
|
||||||
workspacePermissionKeys: mockWorkspacePermissionKeys,
|
workspacePermissionKeys: mockWorkspacePermissionKeys,
|
||||||
}),
|
}))
|
||||||
}))
|
})
|
||||||
|
|
||||||
|
vi.mock('jotai', async (importOriginal) => {
|
||||||
|
const { createAppContextStateJotaiMock } = await import('@/__tests__/utils/mock-app-context-state')
|
||||||
|
return createAppContextStateJotaiMock(importOriginal)
|
||||||
|
})
|
||||||
|
|
||||||
describe('useWorkspacePluginInstallPermission', () => {
|
describe('useWorkspacePluginInstallPermission', () => {
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
|
|||||||
@ -1,12 +1,11 @@
|
|||||||
|
import { useAtomValue } from 'jotai'
|
||||||
import { useMemo } from 'react'
|
import { useMemo } from 'react'
|
||||||
import { useAppContext } from '@/context/app-context'
|
import { langGeniusVersionInfoAtom, workspacePermissionKeysAtom } from '@/context/app-context-state'
|
||||||
import { hasPermission } from '@/utils/permission'
|
import { hasPermission } from '@/utils/permission'
|
||||||
|
|
||||||
const useWorkspacePluginInstallPermission = () => {
|
const useWorkspacePluginInstallPermission = () => {
|
||||||
const {
|
const langGeniusVersionInfo = useAtomValue(langGeniusVersionInfoAtom)
|
||||||
langGeniusVersionInfo,
|
const workspacePermissionKeys = useAtomValue(workspacePermissionKeysAtom)
|
||||||
workspacePermissionKeys,
|
|
||||||
} = useAppContext()
|
|
||||||
|
|
||||||
const canInstallPlugin = useMemo(() => {
|
const canInstallPlugin = useMemo(() => {
|
||||||
return hasPermission(workspacePermissionKeys, 'plugin.install')
|
return hasPermission(workspacePermissionKeys, 'plugin.install')
|
||||||
|
|||||||
@ -57,11 +57,22 @@ vi.mock('../../../base/check-task-status', () => ({
|
|||||||
}),
|
}),
|
||||||
}))
|
}))
|
||||||
|
|
||||||
const mockLangGeniusVersionInfo = { current_version: '1.0.0' }
|
const mockAppContextState = vi.hoisted(() => ({
|
||||||
vi.mock('@/context/app-context', () => ({
|
langGeniusVersionInfoAtom: Symbol('langGeniusVersionInfoAtom'),
|
||||||
useAppContext: () => ({
|
langGeniusVersionInfo: { current_version: '1.0.0' as string | undefined },
|
||||||
langGeniusVersionInfo: mockLangGeniusVersionInfo,
|
}))
|
||||||
}),
|
|
||||||
|
vi.mock('@/context/app-context-state', () => ({
|
||||||
|
langGeniusVersionInfoAtom: mockAppContextState.langGeniusVersionInfoAtom,
|
||||||
|
}))
|
||||||
|
|
||||||
|
vi.mock('jotai', () => ({
|
||||||
|
useAtomValue: (atom: unknown) => {
|
||||||
|
if (atom === mockAppContextState.langGeniusVersionInfoAtom)
|
||||||
|
return mockAppContextState.langGeniusVersionInfo
|
||||||
|
|
||||||
|
throw new Error('Unexpected atom')
|
||||||
|
},
|
||||||
}))
|
}))
|
||||||
|
|
||||||
vi.mock('../../../../card', () => ({
|
vi.mock('../../../../card', () => ({
|
||||||
@ -466,7 +477,7 @@ describe('Install', () => {
|
|||||||
// ================================
|
// ================================
|
||||||
describe('Dify Version Compatibility', () => {
|
describe('Dify Version Compatibility', () => {
|
||||||
it('should not show warning when dify version is compatible', () => {
|
it('should not show warning when dify version is compatible', () => {
|
||||||
mockLangGeniusVersionInfo.current_version = '1.0.0'
|
mockAppContextState.langGeniusVersionInfo.current_version = '1.0.0'
|
||||||
const payload = createMockManifest({ meta: { version: '1.0.0', minimum_dify_version: '0.8.0' } })
|
const payload = createMockManifest({ meta: { version: '1.0.0', minimum_dify_version: '0.8.0' } })
|
||||||
|
|
||||||
render(<Install {...defaultProps} payload={payload} />)
|
render(<Install {...defaultProps} payload={payload} />)
|
||||||
@ -475,7 +486,7 @@ describe('Install', () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
it('should show warning when dify version is incompatible', () => {
|
it('should show warning when dify version is incompatible', () => {
|
||||||
mockLangGeniusVersionInfo.current_version = '1.0.0'
|
mockAppContextState.langGeniusVersionInfo.current_version = '1.0.0'
|
||||||
const payload = createMockManifest({ meta: { version: '1.0.0', minimum_dify_version: '2.0.0' } })
|
const payload = createMockManifest({ meta: { version: '1.0.0', minimum_dify_version: '2.0.0' } })
|
||||||
|
|
||||||
render(<Install {...defaultProps} payload={payload} />)
|
render(<Install {...defaultProps} payload={payload} />)
|
||||||
@ -484,7 +495,7 @@ describe('Install', () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
it('should be compatible when minimum_dify_version is undefined', () => {
|
it('should be compatible when minimum_dify_version is undefined', () => {
|
||||||
mockLangGeniusVersionInfo.current_version = '1.0.0'
|
mockAppContextState.langGeniusVersionInfo.current_version = '1.0.0'
|
||||||
const payload = createMockManifest({ meta: { version: '1.0.0' } })
|
const payload = createMockManifest({ meta: { version: '1.0.0' } })
|
||||||
|
|
||||||
render(<Install {...defaultProps} payload={payload} />)
|
render(<Install {...defaultProps} payload={payload} />)
|
||||||
@ -493,7 +504,7 @@ describe('Install', () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
it('should be compatible when current_version is empty', () => {
|
it('should be compatible when current_version is empty', () => {
|
||||||
mockLangGeniusVersionInfo.current_version = ''
|
mockAppContextState.langGeniusVersionInfo.current_version = ''
|
||||||
const payload = createMockManifest({ meta: { version: '1.0.0', minimum_dify_version: '2.0.0' } })
|
const payload = createMockManifest({ meta: { version: '1.0.0', minimum_dify_version: '2.0.0' } })
|
||||||
|
|
||||||
render(<Install {...defaultProps} payload={payload} />)
|
render(<Install {...defaultProps} payload={payload} />)
|
||||||
@ -503,7 +514,7 @@ describe('Install', () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
it('should be compatible when current_version is undefined', () => {
|
it('should be compatible when current_version is undefined', () => {
|
||||||
mockLangGeniusVersionInfo.current_version = undefined as unknown as string
|
mockAppContextState.langGeniusVersionInfo.current_version = undefined as unknown as string
|
||||||
const payload = createMockManifest({ meta: { version: '1.0.0', minimum_dify_version: '2.0.0' } })
|
const payload = createMockManifest({ meta: { version: '1.0.0', minimum_dify_version: '2.0.0' } })
|
||||||
|
|
||||||
render(<Install {...defaultProps} payload={payload} />)
|
render(<Install {...defaultProps} payload={payload} />)
|
||||||
|
|||||||
@ -3,11 +3,12 @@ import type { FC } from 'react'
|
|||||||
import type { PluginDeclaration } from '../../../types'
|
import type { PluginDeclaration } from '../../../types'
|
||||||
import { Button } from '@langgenius/dify-ui/button'
|
import { Button } from '@langgenius/dify-ui/button'
|
||||||
import { RiLoader2Line } from '@remixicon/react'
|
import { RiLoader2Line } from '@remixicon/react'
|
||||||
|
import { useAtomValue } from 'jotai'
|
||||||
import * as React from 'react'
|
import * as React from 'react'
|
||||||
import { useEffect, useMemo } from 'react'
|
import { useEffect, useMemo } from 'react'
|
||||||
import { Trans, useTranslation } from 'react-i18next'
|
import { Trans, useTranslation } from 'react-i18next'
|
||||||
import useCheckInstalled from '@/app/components/plugins/install-plugin/hooks/use-check-installed'
|
import useCheckInstalled from '@/app/components/plugins/install-plugin/hooks/use-check-installed'
|
||||||
import { useAppContext } from '@/context/app-context'
|
import { langGeniusVersionInfoAtom } from '@/context/app-context-state'
|
||||||
import { uninstallPlugin } from '@/service/plugins'
|
import { uninstallPlugin } from '@/service/plugins'
|
||||||
import { useInstallPackageFromLocal, usePluginTaskList } from '@/service/use-plugins'
|
import { useInstallPackageFromLocal, usePluginTaskList } from '@/service/use-plugins'
|
||||||
import { isEqualOrLaterThanVersion } from '@/utils/semver'
|
import { isEqualOrLaterThanVersion } from '@/utils/semver'
|
||||||
@ -108,7 +109,7 @@ const Installed: FC<Props> = ({
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const { langGeniusVersionInfo } = useAppContext()
|
const langGeniusVersionInfo = useAtomValue(langGeniusVersionInfoAtom)
|
||||||
const isDifyVersionCompatible = useMemo(() => {
|
const isDifyVersionCompatible = useMemo(() => {
|
||||||
if (!langGeniusVersionInfo.current_version)
|
if (!langGeniusVersionInfo.current_version)
|
||||||
return true
|
return true
|
||||||
|
|||||||
@ -60,7 +60,10 @@ const mockStopTaskStatus = vi.fn()
|
|||||||
const mockHandleInstallTaskStart = vi.fn()
|
const mockHandleInstallTaskStart = vi.fn()
|
||||||
let mockPluginDeclaration: { manifest: { meta: { minimum_dify_version: string } } } | undefined
|
let mockPluginDeclaration: { manifest: { meta: { minimum_dify_version: string } } } | undefined
|
||||||
let mockCanInstall = true
|
let mockCanInstall = true
|
||||||
let mockLangGeniusVersionInfo = { current_version: '1.0.0' }
|
const mockAppContextState = vi.hoisted(() => ({
|
||||||
|
langGeniusVersionInfoAtom: Symbol('langGeniusVersionInfoAtom'),
|
||||||
|
langGeniusVersionInfo: { current_version: '1.0.0' as string | null },
|
||||||
|
}))
|
||||||
|
|
||||||
// Mock useCheckInstalled
|
// Mock useCheckInstalled
|
||||||
vi.mock('@/app/components/plugins/install-plugin/hooks/use-check-installed', () => ({
|
vi.mock('@/app/components/plugins/install-plugin/hooks/use-check-installed', () => ({
|
||||||
@ -71,10 +74,17 @@ vi.mock('@/app/components/plugins/install-plugin/hooks/use-check-installed', ()
|
|||||||
}),
|
}),
|
||||||
}))
|
}))
|
||||||
|
|
||||||
vi.mock('@/context/app-context', () => ({
|
vi.mock('@/context/app-context-state', () => ({
|
||||||
useAppContext: () => ({
|
langGeniusVersionInfoAtom: mockAppContextState.langGeniusVersionInfoAtom,
|
||||||
langGeniusVersionInfo: mockLangGeniusVersionInfo,
|
}))
|
||||||
}),
|
|
||||||
|
vi.mock('jotai', () => ({
|
||||||
|
useAtomValue: (atom: unknown) => {
|
||||||
|
if (atom === mockAppContextState.langGeniusVersionInfoAtom)
|
||||||
|
return mockAppContextState.langGeniusVersionInfo
|
||||||
|
|
||||||
|
throw new Error('Unexpected atom')
|
||||||
|
},
|
||||||
}))
|
}))
|
||||||
|
|
||||||
// Mock service hooks
|
// Mock service hooks
|
||||||
@ -104,7 +114,7 @@ vi.mock('../../../base/check-task-status', () => ({
|
|||||||
vi.mock('@/app/components/plugins/install-plugin/hooks/use-plugin-install-permission', () => ({
|
vi.mock('@/app/components/plugins/install-plugin/hooks/use-plugin-install-permission', () => ({
|
||||||
default: () => ({
|
default: () => ({
|
||||||
canInstallPlugin: true,
|
canInstallPlugin: true,
|
||||||
currentDifyVersion: mockLangGeniusVersionInfo.current_version,
|
currentDifyVersion: mockAppContextState.langGeniusVersionInfo.current_version,
|
||||||
}),
|
}),
|
||||||
}))
|
}))
|
||||||
|
|
||||||
@ -170,7 +180,7 @@ describe('Install Component (steps/install.tsx)', () => {
|
|||||||
mockIsLoading = false
|
mockIsLoading = false
|
||||||
mockPluginDeclaration = undefined
|
mockPluginDeclaration = undefined
|
||||||
mockCanInstall = true
|
mockCanInstall = true
|
||||||
mockLangGeniusVersionInfo = { current_version: '1.0.0' }
|
mockAppContextState.langGeniusVersionInfo = { current_version: '1.0.0' }
|
||||||
mockInstallPackageFromMarketPlace.mockResolvedValue({
|
mockInstallPackageFromMarketPlace.mockResolvedValue({
|
||||||
all_installed: false,
|
all_installed: false,
|
||||||
task_id: 'task-123',
|
task_id: 'task-123',
|
||||||
@ -281,7 +291,7 @@ describe('Install Component (steps/install.tsx)', () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
it('should not show warning when dify version is compatible', () => {
|
it('should not show warning when dify version is compatible', () => {
|
||||||
mockLangGeniusVersionInfo = { current_version: '2.0.0' }
|
mockAppContextState.langGeniusVersionInfo = { current_version: '2.0.0' }
|
||||||
mockPluginDeclaration = {
|
mockPluginDeclaration = {
|
||||||
manifest: { meta: { minimum_dify_version: '1.0.0' } },
|
manifest: { meta: { minimum_dify_version: '1.0.0' } },
|
||||||
}
|
}
|
||||||
@ -291,7 +301,7 @@ describe('Install Component (steps/install.tsx)', () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
it('should show warning when dify version is incompatible', () => {
|
it('should show warning when dify version is incompatible', () => {
|
||||||
mockLangGeniusVersionInfo = { current_version: '1.0.0' }
|
mockAppContextState.langGeniusVersionInfo = { current_version: '1.0.0' }
|
||||||
mockPluginDeclaration = {
|
mockPluginDeclaration = {
|
||||||
manifest: { meta: { minimum_dify_version: '2.0.0' } },
|
manifest: { meta: { minimum_dify_version: '2.0.0' } },
|
||||||
}
|
}
|
||||||
@ -749,7 +759,7 @@ describe('Install Component (steps/install.tsx)', () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
it('should handle null current_version in langGeniusVersionInfo', () => {
|
it('should handle null current_version in langGeniusVersionInfo', () => {
|
||||||
mockLangGeniusVersionInfo = { current_version: null as unknown as string }
|
mockAppContextState.langGeniusVersionInfo = { current_version: null as unknown as string }
|
||||||
mockPluginDeclaration = {
|
mockPluginDeclaration = {
|
||||||
manifest: { meta: { minimum_dify_version: '1.0.0' } },
|
manifest: { meta: { minimum_dify_version: '1.0.0' } },
|
||||||
}
|
}
|
||||||
|
|||||||
@ -3,11 +3,12 @@ import type { FC } from 'react'
|
|||||||
import type { InstallPackageResponse, Plugin, PluginManifestInMarket } from '../../../types'
|
import type { InstallPackageResponse, Plugin, PluginManifestInMarket } from '../../../types'
|
||||||
import { Button } from '@langgenius/dify-ui/button'
|
import { Button } from '@langgenius/dify-ui/button'
|
||||||
import { RiLoader2Line } from '@remixicon/react'
|
import { RiLoader2Line } from '@remixicon/react'
|
||||||
|
import { useAtomValue } from 'jotai'
|
||||||
import * as React from 'react'
|
import * as React from 'react'
|
||||||
import { useEffect, useMemo } from 'react'
|
import { useEffect, useMemo } from 'react'
|
||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from 'react-i18next'
|
||||||
import useCheckInstalled from '@/app/components/plugins/install-plugin/hooks/use-check-installed'
|
import useCheckInstalled from '@/app/components/plugins/install-plugin/hooks/use-check-installed'
|
||||||
import { useAppContext } from '@/context/app-context'
|
import { langGeniusVersionInfoAtom } from '@/context/app-context-state'
|
||||||
import { useInstallPackageFromMarketPlace, usePluginDeclarationFromMarketPlace, usePluginTaskList, useUpdatePackageFromMarketPlace } from '@/service/use-plugins'
|
import { useInstallPackageFromMarketPlace, usePluginDeclarationFromMarketPlace, usePluginTaskList, useUpdatePackageFromMarketPlace } from '@/service/use-plugins'
|
||||||
import { isEqualOrLaterThanVersion } from '@/utils/semver'
|
import { isEqualOrLaterThanVersion } from '@/utils/semver'
|
||||||
import Card from '../../../card'
|
import Card from '../../../card'
|
||||||
@ -133,7 +134,7 @@ const Installed: FC<Props> = ({
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const { langGeniusVersionInfo } = useAppContext()
|
const langGeniusVersionInfo = useAtomValue(langGeniusVersionInfoAtom)
|
||||||
const { data: pluginDeclaration } = usePluginDeclarationFromMarketPlace(uniqueIdentifier)
|
const { data: pluginDeclaration } = usePluginDeclarationFromMarketPlace(uniqueIdentifier)
|
||||||
const isDifyVersionCompatible = useMemo(() => {
|
const isDifyVersionCompatible = useMemo(() => {
|
||||||
if (!pluginDeclaration || !langGeniusVersionInfo.current_version)
|
if (!pluginDeclaration || !langGeniusVersionInfo.current_version)
|
||||||
|
|||||||
@ -1,8 +1,9 @@
|
|||||||
'use client'
|
'use client'
|
||||||
|
|
||||||
import type { ReactNode } from 'react'
|
import type { ReactNode } from 'react'
|
||||||
|
import { useAtomValue } from 'jotai'
|
||||||
import { PluginInstallPermissionProvider } from '@/app/components/plugins/install-plugin/components/plugin-install-permission-provider'
|
import { PluginInstallPermissionProvider } from '@/app/components/plugins/install-plugin/components/plugin-install-permission-provider'
|
||||||
import { useAppContext } from '@/context/app-context'
|
import { workspacePermissionKeysAtom } from '@/context/app-context-state'
|
||||||
import { hasPermission } from '@/utils/permission'
|
import { hasPermission } from '@/utils/permission'
|
||||||
|
|
||||||
type MarketplaceInstallPermissionProviderProps = {
|
type MarketplaceInstallPermissionProviderProps = {
|
||||||
@ -12,7 +13,7 @@ type MarketplaceInstallPermissionProviderProps = {
|
|||||||
const MarketplaceInstallPermissionProvider = ({
|
const MarketplaceInstallPermissionProvider = ({
|
||||||
children,
|
children,
|
||||||
}: MarketplaceInstallPermissionProviderProps) => {
|
}: MarketplaceInstallPermissionProviderProps) => {
|
||||||
const { workspacePermissionKeys } = useAppContext()
|
const workspacePermissionKeys = useAtomValue(workspacePermissionKeysAtom)
|
||||||
const canInstallPlugin = hasPermission(workspacePermissionKeys, 'plugin.install')
|
const canInstallPlugin = hasPermission(workspacePermissionKeys, 'plugin.install')
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|||||||
@ -50,6 +50,20 @@ vi.mock('@/context/app-context', () => ({
|
|||||||
}),
|
}),
|
||||||
}))
|
}))
|
||||||
|
|
||||||
|
vi.mock('@/context/app-context-state', async (importOriginal) => {
|
||||||
|
const { createAppContextStateAtomMock } = await import('@/__tests__/utils/mock-app-context-state')
|
||||||
|
return createAppContextStateAtomMock(importOriginal, () => ({
|
||||||
|
userProfile: mockUserProfile,
|
||||||
|
isCurrentWorkspaceManager: mockIsCurrentWorkspaceManager(),
|
||||||
|
workspacePermissionKeys: ['credential.use', 'credential.create', 'credential.manage'],
|
||||||
|
}))
|
||||||
|
})
|
||||||
|
|
||||||
|
vi.mock('jotai', async (importOriginal) => {
|
||||||
|
const { createAppContextStateJotaiMock } = await import('@/__tests__/utils/mock-app-context-state')
|
||||||
|
return createAppContextStateJotaiMock(importOriginal)
|
||||||
|
})
|
||||||
|
|
||||||
vi.mock('@/hooks/use-oauth', () => ({
|
vi.mock('@/hooks/use-oauth', () => ({
|
||||||
openOAuthPopup: vi.fn(),
|
openOAuthPopup: vi.fn(),
|
||||||
}))
|
}))
|
||||||
|
|||||||
@ -30,6 +30,18 @@ vi.mock('@/context/app-context', () => ({
|
|||||||
}),
|
}),
|
||||||
}))
|
}))
|
||||||
|
|
||||||
|
vi.mock('@/context/app-context-state', async (importOriginal) => {
|
||||||
|
const { createAppContextStateAtomMock } = await import('@/__tests__/utils/mock-app-context-state')
|
||||||
|
return createAppContextStateAtomMock(importOriginal, () => ({
|
||||||
|
workspacePermissionKeys: mockAppContext.workspacePermissionKeys,
|
||||||
|
}))
|
||||||
|
})
|
||||||
|
|
||||||
|
vi.mock('jotai', async (importOriginal) => {
|
||||||
|
const { createAppContextStateJotaiMock } = await import('@/__tests__/utils/mock-app-context-state')
|
||||||
|
return createAppContextStateJotaiMock(importOriginal)
|
||||||
|
})
|
||||||
|
|
||||||
vi.mock('@/context/modal-context', () => ({
|
vi.mock('@/context/modal-context', () => ({
|
||||||
useModalContext: () => ({
|
useModalContext: () => ({
|
||||||
setShowAccountSettingModal: mockSetShowAccountSettingModal,
|
setShowAccountSettingModal: mockSetShowAccountSettingModal,
|
||||||
|
|||||||
@ -71,6 +71,18 @@ vi.mock('@/context/app-context', () => ({
|
|||||||
}),
|
}),
|
||||||
}))
|
}))
|
||||||
|
|
||||||
|
vi.mock('@/context/app-context-state', async (importOriginal) => {
|
||||||
|
const { createAppContextStateAtomMock } = await import('@/__tests__/utils/mock-app-context-state')
|
||||||
|
return createAppContextStateAtomMock(importOriginal, () => ({
|
||||||
|
workspacePermissionKeys: mockAppContext.workspacePermissionKeys,
|
||||||
|
}))
|
||||||
|
})
|
||||||
|
|
||||||
|
vi.mock('jotai', async (importOriginal) => {
|
||||||
|
const { createAppContextStateJotaiMock } = await import('@/__tests__/utils/mock-app-context-state')
|
||||||
|
return createAppContextStateJotaiMock(importOriginal)
|
||||||
|
})
|
||||||
|
|
||||||
// Mock service/use-triggers - API service
|
// Mock service/use-triggers - API service
|
||||||
vi.mock('@/service/use-triggers', () => ({
|
vi.mock('@/service/use-triggers', () => ({
|
||||||
useTriggerPluginDynamicOptions: () => ({
|
useTriggerPluginDynamicOptions: () => ({
|
||||||
|
|||||||
@ -86,6 +86,19 @@ vi.mock('@/context/app-context', () => ({
|
|||||||
}) => unknown) => selector(mockAppContext),
|
}) => unknown) => selector(mockAppContext),
|
||||||
}))
|
}))
|
||||||
|
|
||||||
|
vi.mock('@/context/app-context-state', async (importOriginal) => {
|
||||||
|
const { createAppContextStateAtomMock } = await import('@/__tests__/utils/mock-app-context-state')
|
||||||
|
return createAppContextStateAtomMock(importOriginal, () => ({
|
||||||
|
userProfile: mockAppContext.userProfile,
|
||||||
|
workspacePermissionKeys: mockAppContext.workspacePermissionKeys,
|
||||||
|
}))
|
||||||
|
})
|
||||||
|
|
||||||
|
vi.mock('jotai', async (importOriginal) => {
|
||||||
|
const { createAppContextStateJotaiMock } = await import('@/__tests__/utils/mock-app-context-state')
|
||||||
|
return createAppContextStateJotaiMock(importOriginal)
|
||||||
|
})
|
||||||
|
|
||||||
// Mock service/use-triggers
|
// Mock service/use-triggers
|
||||||
vi.mock('@/service/use-triggers', () => ({
|
vi.mock('@/service/use-triggers', () => ({
|
||||||
useTriggerPluginDynamicOptions: () => ({
|
useTriggerPluginDynamicOptions: () => ({
|
||||||
|
|||||||
@ -5,16 +5,18 @@ import { beforeEach, describe, expect, it, vi } from 'vitest'
|
|||||||
import { CredentialTypeEnum } from '../../types'
|
import { CredentialTypeEnum } from '../../types'
|
||||||
import Item from '../item'
|
import Item from '../item'
|
||||||
|
|
||||||
// Item uses useAppContextWithSelector(state => state.userProfile) for the
|
vi.mock('@/context/app-context-state', async (importOriginal) => {
|
||||||
// borrowed-row heuristic; provide a minimal mock so the selector resolves.
|
const { createAppContextStateAtomMock } = await import('@/__tests__/utils/mock-app-context-state')
|
||||||
const mockUserProfile = { id: 'test-user', name: 'Test User', email: 'test@example.com', avatar_url: '' }
|
return createAppContextStateAtomMock(importOriginal, () => ({
|
||||||
vi.mock('@/context/app-context', () => ({
|
userProfile: { id: 'test-user' },
|
||||||
useSelector: (selector: (state: { userProfile: typeof mockUserProfile, workspacePermissionKeys: string[] }) => unknown) =>
|
workspacePermissionKeys: ['credential.use', 'credential.create', 'credential.manage'],
|
||||||
selector({
|
}))
|
||||||
userProfile: mockUserProfile,
|
})
|
||||||
workspacePermissionKeys: ['credential.use', 'credential.create', 'credential.manage'],
|
|
||||||
}),
|
vi.mock('jotai', async (importOriginal) => {
|
||||||
}))
|
const { createAppContextStateJotaiMock } = await import('@/__tests__/utils/mock-app-context-state')
|
||||||
|
return createAppContextStateJotaiMock(importOriginal)
|
||||||
|
})
|
||||||
|
|
||||||
// ==================== Test Utilities ====================
|
// ==================== Test Utilities ====================
|
||||||
|
|
||||||
|
|||||||
@ -6,6 +6,7 @@ import { Tooltip, TooltipContent, TooltipTrigger } from '@langgenius/dify-ui/too
|
|||||||
import {
|
import {
|
||||||
RiInformationLine,
|
RiInformationLine,
|
||||||
} from '@remixicon/react'
|
} from '@remixicon/react'
|
||||||
|
import { useAtomValue } from 'jotai'
|
||||||
import {
|
import {
|
||||||
memo,
|
memo,
|
||||||
useMemo,
|
useMemo,
|
||||||
@ -15,7 +16,7 @@ import { useTranslation } from 'react-i18next'
|
|||||||
import ActionButton from '@/app/components/base/action-button'
|
import ActionButton from '@/app/components/base/action-button'
|
||||||
import Badge from '@/app/components/base/badge'
|
import Badge from '@/app/components/base/badge'
|
||||||
import Input from '@/app/components/base/input'
|
import Input from '@/app/components/base/input'
|
||||||
import { useSelector as useAppContextWithSelector } from '@/context/app-context'
|
import { userProfileIdAtom } from '@/context/app-context-state'
|
||||||
import { useCredentialPermissions } from '@/hooks/use-credential-permissions'
|
import { useCredentialPermissions } from '@/hooks/use-credential-permissions'
|
||||||
import { CredentialTypeEnum } from '../types'
|
import { CredentialTypeEnum } from '../types'
|
||||||
|
|
||||||
@ -58,14 +59,14 @@ const Item = ({
|
|||||||
const { canUseCredential, canManageCredential } = useCredentialPermissions()
|
const { canUseCredential, canManageCredential } = useCredentialPermissions()
|
||||||
const isOAuth = credential.credential_type === CredentialTypeEnum.OAUTH2
|
const isOAuth = credential.credential_type === CredentialTypeEnum.OAUTH2
|
||||||
const isPersonal = credential.visibility === 'only_me'
|
const isPersonal = credential.visibility === 'only_me'
|
||||||
const userProfile = useAppContextWithSelector(state => state.userProfile)
|
const currentUserId = useAtomValue(userProfileIdAtom)
|
||||||
// Borrowed-from-teammate: the backend explicitly flagged this row as another member's
|
// Borrowed-from-teammate: the backend explicitly flagged this row as another member's
|
||||||
// only_me credential, returned only because the current node still references it.
|
// only_me credential, returned only because the current node still references it.
|
||||||
// Fallback heuristic (created_by mismatch on a selected row) is kept for backends
|
// Fallback heuristic (created_by mismatch on a selected row) is kept for backends
|
||||||
// that don't yet emit the flag.
|
// that don't yet emit the flag.
|
||||||
const isSelected = showSelectedIcon && selectedCredentialId === credential.id
|
const isSelected = showSelectedIcon && selectedCredentialId === credential.id
|
||||||
const isConfiguredByOther
|
const isConfiguredByOther
|
||||||
= !!credential.created_by && !!userProfile?.id && credential.created_by !== userProfile.id
|
= !!credential.created_by && !!currentUserId && credential.created_by !== currentUserId
|
||||||
const isBorrowed
|
const isBorrowed
|
||||||
= !!credential.from_other_member || (isSelected && isConfiguredByOther && isPersonal)
|
= !!credential.from_other_member || (isSelected && isConfiguredByOther && isPersonal)
|
||||||
const showSwitchAwayHint = isBorrowed
|
const showSwitchAwayHint = isBorrowed
|
||||||
|
|||||||
@ -1,4 +1,3 @@
|
|||||||
// import { useAppContext } from '@/context/app-context'
|
|
||||||
// import { Button } from '@langgenius/dify-ui/button'
|
// import { Button } from '@langgenius/dify-ui/button'
|
||||||
// import { StatusDot } from '@langgenius/dify-ui/status-dot'
|
// import { StatusDot } from '@langgenius/dify-ui/status-dot'
|
||||||
// import ToolItem from '@/app/components/tools/provider/tool-item'
|
// import ToolItem from '@/app/components/tools/provider/tool-item'
|
||||||
@ -18,7 +17,6 @@ const ActionList = ({
|
|||||||
detail,
|
detail,
|
||||||
}: Props) => {
|
}: Props) => {
|
||||||
const { t } = useTranslation()
|
const { t } = useTranslation()
|
||||||
// const { isCurrentWorkspaceManager } = useAppContext()
|
|
||||||
// const providerBriefInfo = detail.declaration.datasource?.identity
|
// const providerBriefInfo = detail.declaration.datasource?.identity
|
||||||
// const providerKey = `${detail.plugin_id}/${providerBriefInfo?.name}`
|
// const providerKey = `${detail.plugin_id}/${providerBriefInfo?.name}`
|
||||||
const { data: dataSourceList } = useDataSourceList(true)
|
const { data: dataSourceList } = useDataSourceList(true)
|
||||||
|
|||||||
@ -55,13 +55,36 @@ vi.mock('@/app/components/plugins/install-plugin/hooks/use-refresh-plugin-list',
|
|||||||
}))
|
}))
|
||||||
|
|
||||||
const mockLangGeniusVersionInfo = vi.fn(() => ({
|
const mockLangGeniusVersionInfo = vi.fn(() => ({
|
||||||
|
current_env: '',
|
||||||
current_version: '1.0.0',
|
current_version: '1.0.0',
|
||||||
|
latest_version: '',
|
||||||
|
release_date: '',
|
||||||
|
release_notes: '',
|
||||||
|
version: '',
|
||||||
|
can_auto_update: false,
|
||||||
}))
|
}))
|
||||||
vi.mock('@/context/app-context', () => ({
|
|
||||||
useAppContext: () => ({
|
const createLangGeniusVersionInfo = (currentVersion: string) => ({
|
||||||
|
current_env: '',
|
||||||
|
current_version: currentVersion,
|
||||||
|
latest_version: '',
|
||||||
|
release_date: '',
|
||||||
|
release_notes: '',
|
||||||
|
version: '',
|
||||||
|
can_auto_update: false,
|
||||||
|
})
|
||||||
|
|
||||||
|
vi.mock('@/context/app-context-state', async (importOriginal) => {
|
||||||
|
const { createAppContextStateAtomMock } = await import('@/__tests__/utils/mock-app-context-state')
|
||||||
|
return createAppContextStateAtomMock(importOriginal, () => ({
|
||||||
langGeniusVersionInfo: mockLangGeniusVersionInfo(),
|
langGeniusVersionInfo: mockLangGeniusVersionInfo(),
|
||||||
}),
|
}))
|
||||||
}))
|
})
|
||||||
|
|
||||||
|
vi.mock('jotai', async (importOriginal) => {
|
||||||
|
const { createAppContextStateJotaiMock } = await import('@/__tests__/utils/mock-app-context-state')
|
||||||
|
return createAppContextStateJotaiMock(importOriginal)
|
||||||
|
})
|
||||||
|
|
||||||
vi.mock('../action', () => ({
|
vi.mock('../action', () => ({
|
||||||
default: ({ onDelete, pluginName }: { onDelete: () => void, pluginName: string }) => (
|
default: ({ onDelete, pluginName }: { onDelete: () => void, pluginName: string }) => (
|
||||||
@ -162,7 +185,7 @@ describe('PluginItem', () => {
|
|||||||
mockTheme.mockReturnValue('light')
|
mockTheme.mockReturnValue('light')
|
||||||
mockCurrentPluginID.mockReturnValue(undefined)
|
mockCurrentPluginID.mockReturnValue(undefined)
|
||||||
mockEnableMarketplace.mockReturnValue(true)
|
mockEnableMarketplace.mockReturnValue(true)
|
||||||
mockLangGeniusVersionInfo.mockReturnValue({ current_version: '1.0.0' })
|
mockLangGeniusVersionInfo.mockReturnValue(createLangGeniusVersionInfo('1.0.0'))
|
||||||
mockGetValueFromI18nObject.mockImplementation((obj: Record<string, string>) => obj?.en_US || '')
|
mockGetValueFromI18nObject.mockImplementation((obj: Record<string, string>) => obj?.en_US || '')
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -359,7 +382,7 @@ describe('PluginItem', () => {
|
|||||||
describe('Version Compatibility', () => {
|
describe('Version Compatibility', () => {
|
||||||
it('should show warning icon when Dify version is not compatible', () => {
|
it('should show warning icon when Dify version is not compatible', () => {
|
||||||
// Arrange
|
// Arrange
|
||||||
mockLangGeniusVersionInfo.mockReturnValue({ current_version: '0.3.0' })
|
mockLangGeniusVersionInfo.mockReturnValue(createLangGeniusVersionInfo('0.3.0'))
|
||||||
const plugin = createPluginDetail({
|
const plugin = createPluginDetail({
|
||||||
declaration: createPluginDeclaration({
|
declaration: createPluginDeclaration({
|
||||||
meta: { version: '1.0.0', minimum_dify_version: '0.5.0' },
|
meta: { version: '1.0.0', minimum_dify_version: '0.5.0' },
|
||||||
@ -376,7 +399,7 @@ describe('PluginItem', () => {
|
|||||||
|
|
||||||
it('should not show warning when Dify version is compatible', () => {
|
it('should not show warning when Dify version is compatible', () => {
|
||||||
// Arrange
|
// Arrange
|
||||||
mockLangGeniusVersionInfo.mockReturnValue({ current_version: '1.0.0' })
|
mockLangGeniusVersionInfo.mockReturnValue(createLangGeniusVersionInfo('1.0.0'))
|
||||||
const plugin = createPluginDetail({
|
const plugin = createPluginDetail({
|
||||||
declaration: createPluginDeclaration({
|
declaration: createPluginDeclaration({
|
||||||
meta: { version: '1.0.0', minimum_dify_version: '0.5.0' },
|
meta: { version: '1.0.0', minimum_dify_version: '0.5.0' },
|
||||||
@ -393,7 +416,7 @@ describe('PluginItem', () => {
|
|||||||
|
|
||||||
it('should handle missing current_version gracefully', () => {
|
it('should handle missing current_version gracefully', () => {
|
||||||
// Arrange
|
// Arrange
|
||||||
mockLangGeniusVersionInfo.mockReturnValue({ current_version: '' })
|
mockLangGeniusVersionInfo.mockReturnValue(createLangGeniusVersionInfo(''))
|
||||||
const plugin = createPluginDetail()
|
const plugin = createPluginDetail()
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
|
|||||||
@ -11,12 +11,13 @@ import {
|
|||||||
RiLoginCircleLine,
|
RiLoginCircleLine,
|
||||||
} from '@remixicon/react'
|
} from '@remixicon/react'
|
||||||
import { useSuspenseQuery } from '@tanstack/react-query'
|
import { useSuspenseQuery } from '@tanstack/react-query'
|
||||||
|
import { useAtomValue } from 'jotai'
|
||||||
import * as React from 'react'
|
import * as React from 'react'
|
||||||
import { useCallback, useMemo } from 'react'
|
import { useCallback, useMemo } from 'react'
|
||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from 'react-i18next'
|
||||||
import useRefreshPluginList from '@/app/components/plugins/install-plugin/hooks/use-refresh-plugin-list'
|
import useRefreshPluginList from '@/app/components/plugins/install-plugin/hooks/use-refresh-plugin-list'
|
||||||
import { API_PREFIX } from '@/config'
|
import { API_PREFIX } from '@/config'
|
||||||
import { useAppContext } from '@/context/app-context'
|
import { langGeniusVersionInfoAtom } from '@/context/app-context-state'
|
||||||
import { systemFeaturesQueryOptions } from '@/features/system-features/client'
|
import { systemFeaturesQueryOptions } from '@/features/system-features/client'
|
||||||
import { useRenderI18nObject } from '@/hooks/use-i18n'
|
import { useRenderI18nObject } from '@/hooks/use-i18n'
|
||||||
import useTheme from '@/hooks/use-theme'
|
import useTheme from '@/hooks/use-theme'
|
||||||
@ -69,7 +70,7 @@ const PluginItem: FC<Props> = ({
|
|||||||
return [PluginSource.github, PluginSource.marketplace].includes(source) ? author : ''
|
return [PluginSource.github, PluginSource.marketplace].includes(source) ? author : ''
|
||||||
}, [source, author])
|
}, [source, author])
|
||||||
|
|
||||||
const { langGeniusVersionInfo } = useAppContext()
|
const langGeniusVersionInfo = useAtomValue(langGeniusVersionInfoAtom)
|
||||||
|
|
||||||
const isDifyVersionCompatible = useMemo(() => {
|
const isDifyVersionCompatible = useMemo(() => {
|
||||||
if (!langGeniusVersionInfo.current_version)
|
if (!langGeniusVersionInfo.current_version)
|
||||||
|
|||||||
@ -51,11 +51,42 @@ vi.mock('@/context/app-context', () => ({
|
|||||||
useAppContext: () => ({
|
useAppContext: () => ({
|
||||||
isCurrentWorkspaceManager: true,
|
isCurrentWorkspaceManager: true,
|
||||||
isCurrentWorkspaceOwner: false,
|
isCurrentWorkspaceOwner: false,
|
||||||
langGeniusVersionInfo: { current_version: '1.0.0' },
|
langGeniusVersionInfo: {
|
||||||
|
current_env: 'CLOUD',
|
||||||
|
current_version: '1.0.0',
|
||||||
|
latest_version: '1.0.0',
|
||||||
|
version: '1.0.0',
|
||||||
|
release_date: '',
|
||||||
|
release_notes: '',
|
||||||
|
can_auto_update: false,
|
||||||
|
},
|
||||||
workspacePermissionKeys: ['plugin.install', 'plugin.delete', 'plugin.debug', 'plugin.plugin_preferences'],
|
workspacePermissionKeys: ['plugin.install', 'plugin.delete', 'plugin.debug', 'plugin.plugin_preferences'],
|
||||||
}),
|
}),
|
||||||
}))
|
}))
|
||||||
|
|
||||||
|
vi.mock('@/context/app-context-state', async (importOriginal) => {
|
||||||
|
const { createAppContextStateAtomMock } = await import('@/__tests__/utils/mock-app-context-state')
|
||||||
|
return createAppContextStateAtomMock(importOriginal, () => ({
|
||||||
|
isCurrentWorkspaceManager: true,
|
||||||
|
isCurrentWorkspaceOwner: false,
|
||||||
|
langGeniusVersionInfo: {
|
||||||
|
current_env: 'CLOUD',
|
||||||
|
current_version: '1.0.0',
|
||||||
|
latest_version: '1.0.0',
|
||||||
|
version: '1.0.0',
|
||||||
|
release_date: '',
|
||||||
|
release_notes: '',
|
||||||
|
can_auto_update: false,
|
||||||
|
},
|
||||||
|
workspacePermissionKeys: ['plugin.install', 'plugin.delete', 'plugin.debug', 'plugin.plugin_preferences'],
|
||||||
|
}))
|
||||||
|
})
|
||||||
|
|
||||||
|
vi.mock('jotai', async (importOriginal) => {
|
||||||
|
const { createAppContextStateJotaiMock } = await import('@/__tests__/utils/mock-app-context-state')
|
||||||
|
return createAppContextStateJotaiMock(importOriginal)
|
||||||
|
})
|
||||||
|
|
||||||
vi.mock('@/service/use-plugins', () => ({
|
vi.mock('@/service/use-plugins', () => ({
|
||||||
hasPluginPermission: (permission: string | undefined, isAdmin: boolean) => {
|
hasPluginPermission: (permission: string | undefined, isAdmin: boolean) => {
|
||||||
if (!permission)
|
if (!permission)
|
||||||
|
|||||||
@ -1,20 +1,49 @@
|
|||||||
// Import mocks for assertions
|
// Import mocks for assertions
|
||||||
|
import type { AppContextStateMockState } from '@/__tests__/utils/mock-app-context-state'
|
||||||
|
import type { LangGeniusVersionResponse } from '@/models/common'
|
||||||
import { toast } from '@langgenius/dify-ui/toast'
|
import { toast } from '@langgenius/dify-ui/toast'
|
||||||
import { waitFor } from '@testing-library/react'
|
import { waitFor } from '@testing-library/react'
|
||||||
import { beforeEach, describe, expect, it, vi } from 'vitest'
|
import { beforeEach, describe, expect, it, vi } from 'vitest'
|
||||||
import { renderHookWithSystemFeatures as renderHook } from '@/__tests__/utils/mock-system-features'
|
import { renderHookWithSystemFeatures as renderHook } from '@/__tests__/utils/mock-system-features'
|
||||||
import { useAppContext } from '@/context/app-context'
|
|
||||||
|
|
||||||
import { useInvalidateReferenceSettings, useMutationPluginPermissionSettings, useMutationReferenceSettings, usePluginAutoUpgradeSettings, usePluginPermissionSettings } from '@/service/use-plugins'
|
import { useInvalidateReferenceSettings, useMutationPluginPermissionSettings, useMutationReferenceSettings, usePluginAutoUpgradeSettings, usePluginPermissionSettings } from '@/service/use-plugins'
|
||||||
import { PermissionType, PluginCategoryEnum } from '../../types'
|
import { PermissionType, PluginCategoryEnum } from '../../types'
|
||||||
import useReferenceSetting, { useCanInstallPluginFromMarketplace } from '../use-reference-setting'
|
import useReferenceSetting, { useCanInstallPluginFromMarketplace } from '../use-reference-setting'
|
||||||
|
|
||||||
vi.mock('@/context/app-context', async () => {
|
const defaultLangGeniusVersionInfo: LangGeniusVersionResponse = {
|
||||||
const actual = await vi.importActual('@/context/app-context')
|
current_env: '',
|
||||||
return {
|
current_version: '1.0.0',
|
||||||
...actual,
|
latest_version: '',
|
||||||
useAppContext: vi.fn(),
|
release_date: '',
|
||||||
|
release_notes: '',
|
||||||
|
version: '',
|
||||||
|
can_auto_update: false,
|
||||||
|
}
|
||||||
|
|
||||||
|
type MockAppContextState = Omit<AppContextStateMockState, 'langGeniusVersionInfo'> & {
|
||||||
|
langGeniusVersionInfo?: Partial<LangGeniusVersionResponse>
|
||||||
|
}
|
||||||
|
|
||||||
|
let mockAppContextState: AppContextStateMockState = {}
|
||||||
|
|
||||||
|
const setAppContextState = (state: MockAppContextState) => {
|
||||||
|
mockAppContextState = {
|
||||||
|
...state,
|
||||||
|
langGeniusVersionInfo: {
|
||||||
|
...defaultLangGeniusVersionInfo,
|
||||||
|
...state.langGeniusVersionInfo,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
vi.mock('@/context/app-context-state', async (importOriginal) => {
|
||||||
|
const { createAppContextStateAtomMock } = await import('@/__tests__/utils/mock-app-context-state')
|
||||||
|
return createAppContextStateAtomMock(importOriginal, () => mockAppContextState)
|
||||||
|
})
|
||||||
|
|
||||||
|
vi.mock('jotai', async (importOriginal) => {
|
||||||
|
const { createAppContextStateJotaiMock } = await import('@/__tests__/utils/mock-app-context-state')
|
||||||
|
return createAppContextStateJotaiMock(importOriginal)
|
||||||
})
|
})
|
||||||
|
|
||||||
vi.mock('@/service/use-plugins', () => ({
|
vi.mock('@/service/use-plugins', () => ({
|
||||||
@ -33,12 +62,12 @@ describe('useReferenceSetting Hook', () => {
|
|||||||
toastSuccessSpy.mockClear()
|
toastSuccessSpy.mockClear()
|
||||||
|
|
||||||
// Default mocks
|
// Default mocks
|
||||||
vi.mocked(useAppContext).mockReturnValue({
|
setAppContextState({
|
||||||
isCurrentWorkspaceManager: false,
|
isCurrentWorkspaceManager: false,
|
||||||
isCurrentWorkspaceOwner: false,
|
isCurrentWorkspaceOwner: false,
|
||||||
langGeniusVersionInfo: { current_version: '1.0.0', latest_version: '', version: '' },
|
langGeniusVersionInfo: { current_version: '1.0.0', latest_version: '', version: '' },
|
||||||
workspacePermissionKeys: [] as string[],
|
workspacePermissionKeys: [] as string[],
|
||||||
} as ReturnType<typeof useAppContext>)
|
})
|
||||||
|
|
||||||
vi.mocked(usePluginAutoUpgradeSettings).mockReturnValue({
|
vi.mocked(usePluginAutoUpgradeSettings).mockReturnValue({
|
||||||
data: {
|
data: {
|
||||||
@ -109,12 +138,12 @@ describe('useReferenceSetting Hook', () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
it('should allow install and debug when plugin permission keys are present', () => {
|
it('should allow install and debug when plugin permission keys are present', () => {
|
||||||
vi.mocked(useAppContext).mockReturnValue({
|
setAppContextState({
|
||||||
isCurrentWorkspaceManager: false,
|
isCurrentWorkspaceManager: false,
|
||||||
isCurrentWorkspaceOwner: false,
|
isCurrentWorkspaceOwner: false,
|
||||||
langGeniusVersionInfo: { current_version: '1.0.0', latest_version: '', version: '' },
|
langGeniusVersionInfo: { current_version: '1.0.0', latest_version: '', version: '' },
|
||||||
workspacePermissionKeys: ['plugin.install', 'plugin.debug'],
|
workspacePermissionKeys: ['plugin.install', 'plugin.debug'],
|
||||||
} as ReturnType<typeof useAppContext>)
|
})
|
||||||
vi.mocked(usePluginPermissionSettings).mockReturnValue({
|
vi.mocked(usePluginPermissionSettings).mockReturnValue({
|
||||||
data: {
|
data: {
|
||||||
install_permission: PermissionType.everyone,
|
install_permission: PermissionType.everyone,
|
||||||
@ -129,12 +158,12 @@ describe('useReferenceSetting Hook', () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
it('should allow debug for managers with legacy admin permission when RBAC is disabled', () => {
|
it('should allow debug for managers with legacy admin permission when RBAC is disabled', () => {
|
||||||
vi.mocked(useAppContext).mockReturnValue({
|
setAppContextState({
|
||||||
isCurrentWorkspaceManager: true,
|
isCurrentWorkspaceManager: true,
|
||||||
isCurrentWorkspaceOwner: false,
|
isCurrentWorkspaceOwner: false,
|
||||||
langGeniusVersionInfo: { current_version: '1.0.0', latest_version: '', version: '' },
|
langGeniusVersionInfo: { current_version: '1.0.0', latest_version: '', version: '' },
|
||||||
workspacePermissionKeys: [] as string[],
|
workspacePermissionKeys: [] as string[],
|
||||||
} as ReturnType<typeof useAppContext>)
|
})
|
||||||
|
|
||||||
vi.mocked(usePluginPermissionSettings).mockReturnValue({
|
vi.mocked(usePluginPermissionSettings).mockReturnValue({
|
||||||
data: {
|
data: {
|
||||||
@ -150,12 +179,12 @@ describe('useReferenceSetting Hook', () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
it('should allow debug for owners with legacy admin permission when RBAC is disabled', () => {
|
it('should allow debug for owners with legacy admin permission when RBAC is disabled', () => {
|
||||||
vi.mocked(useAppContext).mockReturnValue({
|
setAppContextState({
|
||||||
isCurrentWorkspaceManager: false,
|
isCurrentWorkspaceManager: false,
|
||||||
isCurrentWorkspaceOwner: true,
|
isCurrentWorkspaceOwner: true,
|
||||||
langGeniusVersionInfo: { current_version: '1.0.0', latest_version: '', version: '' },
|
langGeniusVersionInfo: { current_version: '1.0.0', latest_version: '', version: '' },
|
||||||
workspacePermissionKeys: [] as string[],
|
workspacePermissionKeys: [] as string[],
|
||||||
} as ReturnType<typeof useAppContext>)
|
})
|
||||||
|
|
||||||
vi.mocked(usePluginPermissionSettings).mockReturnValue({
|
vi.mocked(usePluginPermissionSettings).mockReturnValue({
|
||||||
data: {
|
data: {
|
||||||
@ -171,12 +200,12 @@ describe('useReferenceSetting Hook', () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
it('should allow debug for normal users when legacy debug permission is everyone and RBAC is disabled', () => {
|
it('should allow debug for normal users when legacy debug permission is everyone and RBAC is disabled', () => {
|
||||||
vi.mocked(useAppContext).mockReturnValue({
|
setAppContextState({
|
||||||
isCurrentWorkspaceManager: false,
|
isCurrentWorkspaceManager: false,
|
||||||
isCurrentWorkspaceOwner: false,
|
isCurrentWorkspaceOwner: false,
|
||||||
langGeniusVersionInfo: { current_version: '1.0.0', latest_version: '', version: '' },
|
langGeniusVersionInfo: { current_version: '1.0.0', latest_version: '', version: '' },
|
||||||
workspacePermissionKeys: ['plugin.install'],
|
workspacePermissionKeys: ['plugin.install'],
|
||||||
} as ReturnType<typeof useAppContext>)
|
})
|
||||||
|
|
||||||
vi.mocked(usePluginPermissionSettings).mockReturnValue({
|
vi.mocked(usePluginPermissionSettings).mockReturnValue({
|
||||||
data: {
|
data: {
|
||||||
@ -194,12 +223,12 @@ describe('useReferenceSetting Hook', () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
it('should use plugin keys even when legacy admin permission is configured and RBAC is enabled', () => {
|
it('should use plugin keys even when legacy admin permission is configured and RBAC is enabled', () => {
|
||||||
vi.mocked(useAppContext).mockReturnValue({
|
setAppContextState({
|
||||||
isCurrentWorkspaceManager: false,
|
isCurrentWorkspaceManager: false,
|
||||||
isCurrentWorkspaceOwner: false,
|
isCurrentWorkspaceOwner: false,
|
||||||
langGeniusVersionInfo: { current_version: '1.0.0', latest_version: '', version: '' },
|
langGeniusVersionInfo: { current_version: '1.0.0', latest_version: '', version: '' },
|
||||||
workspacePermissionKeys: ['plugin.install', 'plugin.debug'],
|
workspacePermissionKeys: ['plugin.install', 'plugin.debug'],
|
||||||
} as ReturnType<typeof useAppContext>)
|
})
|
||||||
|
|
||||||
vi.mocked(usePluginPermissionSettings).mockReturnValue({
|
vi.mocked(usePluginPermissionSettings).mockReturnValue({
|
||||||
data: {
|
data: {
|
||||||
@ -217,12 +246,12 @@ describe('useReferenceSetting Hook', () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
it('should apply legacy noOne plugin permissions when RBAC is disabled', () => {
|
it('should apply legacy noOne plugin permissions when RBAC is disabled', () => {
|
||||||
vi.mocked(useAppContext).mockReturnValue({
|
setAppContextState({
|
||||||
isCurrentWorkspaceManager: true,
|
isCurrentWorkspaceManager: true,
|
||||||
isCurrentWorkspaceOwner: false,
|
isCurrentWorkspaceOwner: false,
|
||||||
langGeniusVersionInfo: { current_version: '1.0.0', latest_version: '', version: '' },
|
langGeniusVersionInfo: { current_version: '1.0.0', latest_version: '', version: '' },
|
||||||
workspacePermissionKeys: ['plugin.install', 'plugin.delete', 'plugin.debug'],
|
workspacePermissionKeys: ['plugin.install', 'plugin.delete', 'plugin.debug'],
|
||||||
} as ReturnType<typeof useAppContext>)
|
})
|
||||||
vi.mocked(usePluginPermissionSettings).mockReturnValue({
|
vi.mocked(usePluginPermissionSettings).mockReturnValue({
|
||||||
data: {
|
data: {
|
||||||
install_permission: PermissionType.noOne,
|
install_permission: PermissionType.noOne,
|
||||||
@ -245,12 +274,12 @@ describe('useReferenceSetting Hook', () => {
|
|||||||
|
|
||||||
describe('canSetPermissions', () => {
|
describe('canSetPermissions', () => {
|
||||||
it('should be true with plugin preferences permission when RBAC is disabled', () => {
|
it('should be true with plugin preferences permission when RBAC is disabled', () => {
|
||||||
vi.mocked(useAppContext).mockReturnValue({
|
setAppContextState({
|
||||||
isCurrentWorkspaceManager: false,
|
isCurrentWorkspaceManager: false,
|
||||||
isCurrentWorkspaceOwner: false,
|
isCurrentWorkspaceOwner: false,
|
||||||
langGeniusVersionInfo: { current_version: '1.0.0', latest_version: '', version: '' },
|
langGeniusVersionInfo: { current_version: '1.0.0', latest_version: '', version: '' },
|
||||||
workspacePermissionKeys: ['plugin.plugin_preferences'],
|
workspacePermissionKeys: ['plugin.plugin_preferences'],
|
||||||
} as ReturnType<typeof useAppContext>)
|
})
|
||||||
|
|
||||||
const { result } = renderHook(() => useReferenceSetting(PluginCategoryEnum.tool))
|
const { result } = renderHook(() => useReferenceSetting(PluginCategoryEnum.tool))
|
||||||
|
|
||||||
@ -258,12 +287,12 @@ describe('useReferenceSetting Hook', () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
it('should be false when RBAC is enabled even with plugin preferences permission', () => {
|
it('should be false when RBAC is enabled even with plugin preferences permission', () => {
|
||||||
vi.mocked(useAppContext).mockReturnValue({
|
setAppContextState({
|
||||||
isCurrentWorkspaceManager: false,
|
isCurrentWorkspaceManager: false,
|
||||||
isCurrentWorkspaceOwner: true,
|
isCurrentWorkspaceOwner: true,
|
||||||
langGeniusVersionInfo: { current_version: '1.0.0', latest_version: '', version: '' },
|
langGeniusVersionInfo: { current_version: '1.0.0', latest_version: '', version: '' },
|
||||||
workspacePermissionKeys: ['plugin.plugin_preferences'],
|
workspacePermissionKeys: ['plugin.plugin_preferences'],
|
||||||
} as ReturnType<typeof useAppContext>)
|
})
|
||||||
|
|
||||||
const { result } = renderHook(() => useReferenceSetting(PluginCategoryEnum.tool), {
|
const { result } = renderHook(() => useReferenceSetting(PluginCategoryEnum.tool), {
|
||||||
systemFeatures: { rbac_enabled: true },
|
systemFeatures: { rbac_enabled: true },
|
||||||
@ -274,12 +303,12 @@ describe('useReferenceSetting Hook', () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
it('should be false without plugin preferences permission', () => {
|
it('should be false without plugin preferences permission', () => {
|
||||||
vi.mocked(useAppContext).mockReturnValue({
|
setAppContextState({
|
||||||
isCurrentWorkspaceManager: true,
|
isCurrentWorkspaceManager: true,
|
||||||
isCurrentWorkspaceOwner: false,
|
isCurrentWorkspaceOwner: false,
|
||||||
langGeniusVersionInfo: { current_version: '1.0.0', latest_version: '', version: '' },
|
langGeniusVersionInfo: { current_version: '1.0.0', latest_version: '', version: '' },
|
||||||
workspacePermissionKeys: [] as string[],
|
workspacePermissionKeys: [] as string[],
|
||||||
} as ReturnType<typeof useAppContext>)
|
})
|
||||||
|
|
||||||
const { result } = renderHook(() => useReferenceSetting(PluginCategoryEnum.tool))
|
const { result } = renderHook(() => useReferenceSetting(PluginCategoryEnum.tool))
|
||||||
|
|
||||||
@ -353,12 +382,12 @@ describe('useReferenceSetting Hook', () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
it('should keep permission key access available when reference setting data is still loading', () => {
|
it('should keep permission key access available when reference setting data is still loading', () => {
|
||||||
vi.mocked(useAppContext).mockReturnValue({
|
setAppContextState({
|
||||||
isCurrentWorkspaceManager: false,
|
isCurrentWorkspaceManager: false,
|
||||||
isCurrentWorkspaceOwner: false,
|
isCurrentWorkspaceOwner: false,
|
||||||
langGeniusVersionInfo: { current_version: '1.0.0', latest_version: '', version: '' },
|
langGeniusVersionInfo: { current_version: '1.0.0', latest_version: '', version: '' },
|
||||||
workspacePermissionKeys: ['plugin.install', 'plugin.debug'],
|
workspacePermissionKeys: ['plugin.install', 'plugin.debug'],
|
||||||
} as ReturnType<typeof useAppContext>)
|
})
|
||||||
vi.mocked(usePluginAutoUpgradeSettings).mockReturnValue({
|
vi.mocked(usePluginAutoUpgradeSettings).mockReturnValue({
|
||||||
data: undefined,
|
data: undefined,
|
||||||
} as unknown as ReturnType<typeof usePluginAutoUpgradeSettings>)
|
} as unknown as ReturnType<typeof usePluginAutoUpgradeSettings>)
|
||||||
@ -371,13 +400,13 @@ describe('useReferenceSetting Hook', () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
it('should keep permission state loading while workspace permission keys are loading', () => {
|
it('should keep permission state loading while workspace permission keys are loading', () => {
|
||||||
vi.mocked(useAppContext).mockReturnValue({
|
setAppContextState({
|
||||||
isCurrentWorkspaceManager: false,
|
isCurrentWorkspaceManager: false,
|
||||||
isCurrentWorkspaceOwner: false,
|
isCurrentWorkspaceOwner: false,
|
||||||
isLoadingWorkspacePermissionKeys: true,
|
isLoadingWorkspacePermissionKeys: true,
|
||||||
langGeniusVersionInfo: { current_version: '1.0.0', latest_version: '', version: '' },
|
langGeniusVersionInfo: { current_version: '1.0.0', latest_version: '', version: '' },
|
||||||
workspacePermissionKeys: [] as string[],
|
workspacePermissionKeys: [] as string[],
|
||||||
} as ReturnType<typeof useAppContext>)
|
})
|
||||||
|
|
||||||
const { result } = renderHook(() => useReferenceSetting(PluginCategoryEnum.tool))
|
const { result } = renderHook(() => useReferenceSetting(PluginCategoryEnum.tool))
|
||||||
|
|
||||||
@ -386,13 +415,13 @@ describe('useReferenceSetting Hook', () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
it('should keep permission state loading while current workspace is loading', () => {
|
it('should keep permission state loading while current workspace is loading', () => {
|
||||||
vi.mocked(useAppContext).mockReturnValue({
|
setAppContextState({
|
||||||
isCurrentWorkspaceManager: false,
|
isCurrentWorkspaceManager: false,
|
||||||
isCurrentWorkspaceOwner: false,
|
isCurrentWorkspaceOwner: false,
|
||||||
isLoadingCurrentWorkspace: true,
|
isLoadingCurrentWorkspace: true,
|
||||||
langGeniusVersionInfo: { current_version: '1.0.0', latest_version: '', version: '' },
|
langGeniusVersionInfo: { current_version: '1.0.0', latest_version: '', version: '' },
|
||||||
workspacePermissionKeys: ['plugin.install'],
|
workspacePermissionKeys: ['plugin.install'],
|
||||||
} as ReturnType<typeof useAppContext>)
|
})
|
||||||
|
|
||||||
const { result } = renderHook(() => useReferenceSetting(PluginCategoryEnum.tool))
|
const { result } = renderHook(() => useReferenceSetting(PluginCategoryEnum.tool))
|
||||||
|
|
||||||
@ -402,7 +431,7 @@ describe('useReferenceSetting Hook', () => {
|
|||||||
|
|
||||||
describe('RBAC permissions', () => {
|
describe('RBAC permissions', () => {
|
||||||
it('should use workspace permission keys when RBAC is enabled', () => {
|
it('should use workspace permission keys when RBAC is enabled', () => {
|
||||||
vi.mocked(useAppContext).mockReturnValue({
|
setAppContextState({
|
||||||
isCurrentWorkspaceManager: false,
|
isCurrentWorkspaceManager: false,
|
||||||
isCurrentWorkspaceOwner: false,
|
isCurrentWorkspaceOwner: false,
|
||||||
langGeniusVersionInfo: { current_version: '1.0.0', latest_version: '', version: '' },
|
langGeniusVersionInfo: { current_version: '1.0.0', latest_version: '', version: '' },
|
||||||
@ -412,7 +441,7 @@ describe('useReferenceSetting Hook', () => {
|
|||||||
'plugin.debug',
|
'plugin.debug',
|
||||||
'plugin.plugin_preferences',
|
'plugin.plugin_preferences',
|
||||||
],
|
],
|
||||||
} as ReturnType<typeof useAppContext>)
|
})
|
||||||
vi.mocked(usePluginPermissionSettings).mockReturnValue({
|
vi.mocked(usePluginPermissionSettings).mockReturnValue({
|
||||||
data: {
|
data: {
|
||||||
install_permission: PermissionType.noOne,
|
install_permission: PermissionType.noOne,
|
||||||
@ -435,12 +464,12 @@ describe('useReferenceSetting Hook', () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
it('should ignore legacy plugin permission settings when RBAC is enabled', () => {
|
it('should ignore legacy plugin permission settings when RBAC is enabled', () => {
|
||||||
vi.mocked(useAppContext).mockReturnValue({
|
setAppContextState({
|
||||||
isCurrentWorkspaceManager: true,
|
isCurrentWorkspaceManager: true,
|
||||||
isCurrentWorkspaceOwner: false,
|
isCurrentWorkspaceOwner: false,
|
||||||
langGeniusVersionInfo: { current_version: '1.0.0', latest_version: '', version: '' },
|
langGeniusVersionInfo: { current_version: '1.0.0', latest_version: '', version: '' },
|
||||||
workspacePermissionKeys: [] as string[],
|
workspacePermissionKeys: [] as string[],
|
||||||
} as ReturnType<typeof useAppContext>)
|
})
|
||||||
|
|
||||||
const { result } = renderHook(() => useReferenceSetting(PluginCategoryEnum.tool), {
|
const { result } = renderHook(() => useReferenceSetting(PluginCategoryEnum.tool), {
|
||||||
systemFeatures: { rbac_enabled: true },
|
systemFeatures: { rbac_enabled: true },
|
||||||
@ -462,12 +491,12 @@ describe('useCanInstallPluginFromMarketplace Hook', () => {
|
|||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
vi.clearAllMocks()
|
vi.clearAllMocks()
|
||||||
|
|
||||||
vi.mocked(useAppContext).mockReturnValue({
|
setAppContextState({
|
||||||
isCurrentWorkspaceManager: true,
|
isCurrentWorkspaceManager: true,
|
||||||
isCurrentWorkspaceOwner: false,
|
isCurrentWorkspaceOwner: false,
|
||||||
langGeniusVersionInfo: { current_version: '1.0.0', latest_version: '', version: '' },
|
langGeniusVersionInfo: { current_version: '1.0.0', latest_version: '', version: '' },
|
||||||
workspacePermissionKeys: ['plugin.install'],
|
workspacePermissionKeys: ['plugin.install'],
|
||||||
} as ReturnType<typeof useAppContext>)
|
})
|
||||||
|
|
||||||
vi.mocked(usePluginPermissionSettings).mockReturnValue({
|
vi.mocked(usePluginPermissionSettings).mockReturnValue({
|
||||||
data: {
|
data: {
|
||||||
@ -501,12 +530,12 @@ describe('useCanInstallPluginFromMarketplace Hook', () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
it('should return false without plugin.install', () => {
|
it('should return false without plugin.install', () => {
|
||||||
vi.mocked(useAppContext).mockReturnValue({
|
setAppContextState({
|
||||||
isCurrentWorkspaceManager: true,
|
isCurrentWorkspaceManager: true,
|
||||||
isCurrentWorkspaceOwner: false,
|
isCurrentWorkspaceOwner: false,
|
||||||
langGeniusVersionInfo: { current_version: '1.0.0', latest_version: '', version: '' },
|
langGeniusVersionInfo: { current_version: '1.0.0', latest_version: '', version: '' },
|
||||||
workspacePermissionKeys: [] as string[],
|
workspacePermissionKeys: [] as string[],
|
||||||
} as ReturnType<typeof useAppContext>)
|
})
|
||||||
|
|
||||||
const { result } = renderHook(() => useCanInstallPluginFromMarketplace(), {
|
const { result } = renderHook(() => useCanInstallPluginFromMarketplace(), {
|
||||||
systemFeatures: { enable_marketplace: true },
|
systemFeatures: { enable_marketplace: true },
|
||||||
@ -516,12 +545,12 @@ describe('useCanInstallPluginFromMarketplace Hook', () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
it('should return false when both marketplace is disabled and plugin.install is missing', () => {
|
it('should return false when both marketplace is disabled and plugin.install is missing', () => {
|
||||||
vi.mocked(useAppContext).mockReturnValue({
|
setAppContextState({
|
||||||
isCurrentWorkspaceManager: true,
|
isCurrentWorkspaceManager: true,
|
||||||
isCurrentWorkspaceOwner: false,
|
isCurrentWorkspaceOwner: false,
|
||||||
langGeniusVersionInfo: { current_version: '1.0.0', latest_version: '', version: '' },
|
langGeniusVersionInfo: { current_version: '1.0.0', latest_version: '', version: '' },
|
||||||
workspacePermissionKeys: [] as string[],
|
workspacePermissionKeys: [] as string[],
|
||||||
} as ReturnType<typeof useAppContext>)
|
})
|
||||||
|
|
||||||
const { result } = renderHook(() => useCanInstallPluginFromMarketplace(), {
|
const { result } = renderHook(() => useCanInstallPluginFromMarketplace(), {
|
||||||
systemFeatures: { enable_marketplace: false },
|
systemFeatures: { enable_marketplace: false },
|
||||||
@ -558,12 +587,12 @@ describe('useCanInstallPluginFromMarketplace Hook', () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
it('should use plugin.install when marketplace and RBAC are enabled', () => {
|
it('should use plugin.install when marketplace and RBAC are enabled', () => {
|
||||||
vi.mocked(useAppContext).mockReturnValue({
|
setAppContextState({
|
||||||
isCurrentWorkspaceManager: false,
|
isCurrentWorkspaceManager: false,
|
||||||
isCurrentWorkspaceOwner: false,
|
isCurrentWorkspaceOwner: false,
|
||||||
langGeniusVersionInfo: { current_version: '1.0.0', latest_version: '', version: '' },
|
langGeniusVersionInfo: { current_version: '1.0.0', latest_version: '', version: '' },
|
||||||
workspacePermissionKeys: ['plugin.install'],
|
workspacePermissionKeys: ['plugin.install'],
|
||||||
} as ReturnType<typeof useAppContext>)
|
})
|
||||||
vi.mocked(usePluginPermissionSettings).mockReturnValue({
|
vi.mocked(usePluginPermissionSettings).mockReturnValue({
|
||||||
data: {
|
data: {
|
||||||
install_permission: PermissionType.noOne,
|
install_permission: PermissionType.noOne,
|
||||||
|
|||||||
@ -1,16 +1,23 @@
|
|||||||
import type { PluginCategoryEnum } from '../types'
|
import type { PluginCategoryEnum } from '../types'
|
||||||
import { toast } from '@langgenius/dify-ui/toast'
|
import { toast } from '@langgenius/dify-ui/toast'
|
||||||
import { useSuspenseQuery } from '@tanstack/react-query'
|
import { useSuspenseQuery } from '@tanstack/react-query'
|
||||||
|
import { useAtomValue } from 'jotai'
|
||||||
import { useMemo } from 'react'
|
import { useMemo } from 'react'
|
||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from 'react-i18next'
|
||||||
import { useAppContext } from '@/context/app-context'
|
import {
|
||||||
|
currentWorkspaceLoadingAtom,
|
||||||
|
langGeniusVersionInfoAtom,
|
||||||
|
workspacePermissionKeysAtom,
|
||||||
|
workspacePermissionKeysLoadingAtom,
|
||||||
|
workspaceRoleFlagsAtom,
|
||||||
|
} from '@/context/app-context-state'
|
||||||
import { systemFeaturesQueryOptions } from '@/features/system-features/client'
|
import { systemFeaturesQueryOptions } from '@/features/system-features/client'
|
||||||
import { useInvalidateReferenceSettings, useMutationPluginPermissionSettings, useMutationReferenceSettings, usePluginAutoUpgradeSettings, usePluginPermissionSettings } from '@/service/use-plugins'
|
import { useInvalidateReferenceSettings, useMutationPluginPermissionSettings, useMutationReferenceSettings, usePluginAutoUpgradeSettings, usePluginPermissionSettings } from '@/service/use-plugins'
|
||||||
import { hasPermission } from '@/utils/permission'
|
import { hasPermission } from '@/utils/permission'
|
||||||
import { hasLegacyPluginPermissionAccess } from '../plugin-permissions'
|
import { hasLegacyPluginPermissionAccess } from '../plugin-permissions'
|
||||||
|
|
||||||
const useCanSetPluginSettings = () => {
|
const useCanSetPluginSettings = () => {
|
||||||
const { workspacePermissionKeys } = useAppContext()
|
const workspacePermissionKeys = useAtomValue(workspacePermissionKeysAtom)
|
||||||
const { data: rbacEnabled } = useSuspenseQuery({
|
const { data: rbacEnabled } = useSuspenseQuery({
|
||||||
...systemFeaturesQueryOptions(),
|
...systemFeaturesQueryOptions(),
|
||||||
select: s => s.rbac_enabled,
|
select: s => s.rbac_enabled,
|
||||||
@ -28,11 +35,11 @@ export const usePluginSettingsAccess = () => {
|
|||||||
const {
|
const {
|
||||||
isCurrentWorkspaceManager,
|
isCurrentWorkspaceManager,
|
||||||
isCurrentWorkspaceOwner,
|
isCurrentWorkspaceOwner,
|
||||||
isLoadingCurrentWorkspace,
|
} = useAtomValue(workspaceRoleFlagsAtom)
|
||||||
isLoadingWorkspacePermissionKeys,
|
const isLoadingCurrentWorkspace = useAtomValue(currentWorkspaceLoadingAtom)
|
||||||
workspacePermissionKeys,
|
const isLoadingWorkspacePermissionKeys = useAtomValue(workspacePermissionKeysLoadingAtom)
|
||||||
langGeniusVersionInfo,
|
const workspacePermissionKeys = useAtomValue(workspacePermissionKeysAtom)
|
||||||
} = useAppContext()
|
const langGeniusVersionInfo = useAtomValue(langGeniusVersionInfoAtom)
|
||||||
const { data: rbacEnabled } = useSuspenseQuery({
|
const { data: rbacEnabled } = useSuspenseQuery({
|
||||||
...systemFeaturesQueryOptions(),
|
...systemFeaturesQueryOptions(),
|
||||||
select: s => s.rbac_enabled,
|
select: s => s.rbac_enabled,
|
||||||
@ -125,7 +132,11 @@ export const useCanInstallPluginFromMarketplace = () => {
|
|||||||
const { data: systemFeatures } = useSuspenseQuery(systemFeaturesQueryOptions())
|
const { data: systemFeatures } = useSuspenseQuery(systemFeaturesQueryOptions())
|
||||||
const marketplaceAccess = systemFeatures.enable_marketplace
|
const marketplaceAccess = systemFeatures.enable_marketplace
|
||||||
const rbacEnabled = systemFeatures.rbac_enabled
|
const rbacEnabled = systemFeatures.rbac_enabled
|
||||||
const { isCurrentWorkspaceManager, isCurrentWorkspaceOwner, workspacePermissionKeys } = useAppContext()
|
const {
|
||||||
|
isCurrentWorkspaceManager,
|
||||||
|
isCurrentWorkspaceOwner,
|
||||||
|
} = useAtomValue(workspaceRoleFlagsAtom)
|
||||||
|
const workspacePermissionKeys = useAtomValue(workspacePermissionKeysAtom)
|
||||||
const permissionQuery = usePluginPermissionSettings()
|
const permissionQuery = usePluginPermissionSettings()
|
||||||
const { data: permissions } = permissionQuery
|
const { data: permissions } = permissionQuery
|
||||||
const legacyCanInstallPlugin = hasLegacyPluginPermissionAccess({
|
const legacyCanInstallPlugin = hasLegacyPluginPermissionAccess({
|
||||||
|
|||||||
@ -1,16 +1,17 @@
|
|||||||
'use client'
|
'use client'
|
||||||
|
|
||||||
import { useSelector as useAppContextSelector } from '@/context/app-context'
|
import { useAtomValue } from 'jotai'
|
||||||
|
import { workspacePermissionKeysAtom } from '@/context/app-context-state'
|
||||||
import { hasPermission } from '@/utils/permission'
|
import { hasPermission } from '@/utils/permission'
|
||||||
|
|
||||||
export const useCanManageTools = () => {
|
export const useCanManageTools = () => {
|
||||||
const workspacePermissionKeys = useAppContextSelector(state => state.workspacePermissionKeys)
|
const workspacePermissionKeys = useAtomValue(workspacePermissionKeysAtom)
|
||||||
|
|
||||||
return hasPermission(workspacePermissionKeys, 'tool.manage')
|
return hasPermission(workspacePermissionKeys, 'tool.manage')
|
||||||
}
|
}
|
||||||
|
|
||||||
export const useCanManageMCP = () => {
|
export const useCanManageMCP = () => {
|
||||||
const workspacePermissionKeys = useAppContextSelector(state => state.workspacePermissionKeys)
|
const workspacePermissionKeys = useAtomValue(workspacePermissionKeysAtom)
|
||||||
|
|
||||||
return hasPermission(workspacePermissionKeys, 'mcp.manage')
|
return hasPermission(workspacePermissionKeys, 'mcp.manage')
|
||||||
}
|
}
|
||||||
|
|||||||
@ -40,14 +40,30 @@ vi.mock('../modal', () => ({
|
|||||||
},
|
},
|
||||||
}))
|
}))
|
||||||
|
|
||||||
let mockWorkspacePermissionKeys: string[] = ['mcp.manage']
|
const mockAppContextState = vi.hoisted(() => ({
|
||||||
|
workspacePermissionKeys: ['mcp.manage'] as string[],
|
||||||
|
workspacePermissionKeysAtom: Symbol('workspacePermissionKeysAtom'),
|
||||||
|
}))
|
||||||
|
|
||||||
vi.mock('@/context/app-context', () => ({
|
vi.mock('@/context/app-context', () => ({
|
||||||
useSelector: (selector: (state: { workspacePermissionKeys: string[] }) => unknown) => selector({
|
useSelector: (selector: (state: { workspacePermissionKeys: string[] }) => unknown) => selector({
|
||||||
workspacePermissionKeys: mockWorkspacePermissionKeys,
|
workspacePermissionKeys: mockAppContextState.workspacePermissionKeys,
|
||||||
}),
|
}),
|
||||||
}))
|
}))
|
||||||
|
|
||||||
|
vi.mock('@/context/app-context-state', () => ({
|
||||||
|
workspacePermissionKeysAtom: mockAppContextState.workspacePermissionKeysAtom,
|
||||||
|
}))
|
||||||
|
|
||||||
|
vi.mock('jotai', () => ({
|
||||||
|
useAtomValue: (atom: unknown) => {
|
||||||
|
if (atom === mockAppContextState.workspacePermissionKeysAtom)
|
||||||
|
return mockAppContextState.workspacePermissionKeys
|
||||||
|
|
||||||
|
throw new Error('Unexpected atom')
|
||||||
|
},
|
||||||
|
}))
|
||||||
|
|
||||||
// Mock the plugins service
|
// Mock the plugins service
|
||||||
vi.mock('@/service/use-plugins', () => ({
|
vi.mock('@/service/use-plugins', () => ({
|
||||||
useInstalledPluginList: () => ({
|
useInstalledPluginList: () => ({
|
||||||
@ -84,7 +100,7 @@ describe('NewMCPCard', () => {
|
|||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
mockCreateMCP.mockClear()
|
mockCreateMCP.mockClear()
|
||||||
mockWorkspacePermissionKeys = ['mcp.manage']
|
mockAppContextState.workspacePermissionKeys = ['mcp.manage']
|
||||||
})
|
})
|
||||||
|
|
||||||
describe('Rendering', () => {
|
describe('Rendering', () => {
|
||||||
@ -152,7 +168,7 @@ describe('NewMCPCard', () => {
|
|||||||
|
|
||||||
describe('mcp.manage Permission', () => {
|
describe('mcp.manage Permission', () => {
|
||||||
it('should not render card when user lacks mcp.manage', () => {
|
it('should not render card when user lacks mcp.manage', () => {
|
||||||
mockWorkspacePermissionKeys = []
|
mockAppContextState.workspacePermissionKeys = []
|
||||||
|
|
||||||
render(<NewMCPCard {...defaultProps} />, { wrapper: createWrapper() })
|
render(<NewMCPCard {...defaultProps} />, { wrapper: createWrapper() })
|
||||||
|
|
||||||
@ -160,7 +176,7 @@ describe('NewMCPCard', () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
it('should not render toolbar button when user lacks mcp.manage', () => {
|
it('should not render toolbar button when user lacks mcp.manage', () => {
|
||||||
mockWorkspacePermissionKeys = []
|
mockAppContextState.workspacePermissionKeys = []
|
||||||
|
|
||||||
render(<NewMCPButton {...defaultProps} />, { wrapper: createWrapper() })
|
render(<NewMCPButton {...defaultProps} />, { wrapper: createWrapper() })
|
||||||
|
|
||||||
|
|||||||
@ -15,7 +15,10 @@ const mockRefetch = vi.fn()
|
|||||||
const mockUseAllToolProviders = vi.fn()
|
const mockUseAllToolProviders = vi.fn()
|
||||||
let mockProviders: MockProvider[] = []
|
let mockProviders: MockProvider[] = []
|
||||||
let mockIsLoadingToolProviders = false
|
let mockIsLoadingToolProviders = false
|
||||||
let mockWorkspacePermissionKeys = ['mcp.manage']
|
const mockAppContextState = vi.hoisted(() => ({
|
||||||
|
workspacePermissionKeys: ['mcp.manage'] as string[],
|
||||||
|
workspacePermissionKeysAtom: Symbol('workspacePermissionKeysAtom'),
|
||||||
|
}))
|
||||||
|
|
||||||
vi.mock('@/service/use-tools', () => ({
|
vi.mock('@/service/use-tools', () => ({
|
||||||
useAllToolProviders: (enabled?: boolean) => {
|
useAllToolProviders: (enabled?: boolean) => {
|
||||||
@ -30,10 +33,23 @@ vi.mock('@/service/use-tools', () => ({
|
|||||||
|
|
||||||
vi.mock('@/context/app-context', () => ({
|
vi.mock('@/context/app-context', () => ({
|
||||||
useSelector: (selector: (state: { workspacePermissionKeys: string[] }) => unknown) => selector({
|
useSelector: (selector: (state: { workspacePermissionKeys: string[] }) => unknown) => selector({
|
||||||
workspacePermissionKeys: mockWorkspacePermissionKeys,
|
workspacePermissionKeys: mockAppContextState.workspacePermissionKeys,
|
||||||
}),
|
}),
|
||||||
}))
|
}))
|
||||||
|
|
||||||
|
vi.mock('@/context/app-context-state', () => ({
|
||||||
|
workspacePermissionKeysAtom: mockAppContextState.workspacePermissionKeysAtom,
|
||||||
|
}))
|
||||||
|
|
||||||
|
vi.mock('jotai', () => ({
|
||||||
|
useAtomValue: (atom: unknown) => {
|
||||||
|
if (atom === mockAppContextState.workspacePermissionKeysAtom)
|
||||||
|
return mockAppContextState.workspacePermissionKeys
|
||||||
|
|
||||||
|
throw new Error('Unexpected atom')
|
||||||
|
},
|
||||||
|
}))
|
||||||
|
|
||||||
vi.mock('@/app/components/tools/provider/tool-card-skeleton', () => ({
|
vi.mock('@/app/components/tools/provider/tool-card-skeleton', () => ({
|
||||||
default: ({ variant }: { variant?: string }) => (
|
default: ({ variant }: { variant?: string }) => (
|
||||||
<>
|
<>
|
||||||
@ -89,7 +105,7 @@ describe('MCPList', () => {
|
|||||||
vi.useFakeTimers()
|
vi.useFakeTimers()
|
||||||
mockProviders = []
|
mockProviders = []
|
||||||
mockIsLoadingToolProviders = false
|
mockIsLoadingToolProviders = false
|
||||||
mockWorkspacePermissionKeys = ['mcp.manage']
|
mockAppContextState.workspacePermissionKeys = ['mcp.manage']
|
||||||
mockRefetch.mockResolvedValue(undefined)
|
mockRefetch.mockResolvedValue(undefined)
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -111,7 +127,7 @@ describe('MCPList', () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
it('should render providers read-only when user lacks mcp.manage', () => {
|
it('should render providers read-only when user lacks mcp.manage', () => {
|
||||||
mockWorkspacePermissionKeys = []
|
mockAppContextState.workspacePermissionKeys = []
|
||||||
mockProviders = [
|
mockProviders = [
|
||||||
{ id: '1', name: 'Provider 1', type: 'mcp' },
|
{ id: '1', name: 'Provider 1', type: 'mcp' },
|
||||||
]
|
]
|
||||||
|
|||||||
@ -81,14 +81,30 @@ vi.mock('../detail/operation-dropdown', () => ({
|
|||||||
),
|
),
|
||||||
}))
|
}))
|
||||||
|
|
||||||
let mockWorkspacePermissionKeys: string[] = ['mcp.manage']
|
const mockAppContextState = vi.hoisted(() => ({
|
||||||
|
workspacePermissionKeys: ['mcp.manage'] as string[],
|
||||||
|
workspacePermissionKeysAtom: Symbol('workspacePermissionKeysAtom'),
|
||||||
|
}))
|
||||||
|
|
||||||
vi.mock('@/context/app-context', () => ({
|
vi.mock('@/context/app-context', () => ({
|
||||||
useSelector: (selector: (state: { workspacePermissionKeys: string[] }) => unknown) => selector({
|
useSelector: (selector: (state: { workspacePermissionKeys: string[] }) => unknown) => selector({
|
||||||
workspacePermissionKeys: mockWorkspacePermissionKeys,
|
workspacePermissionKeys: mockAppContextState.workspacePermissionKeys,
|
||||||
}),
|
}),
|
||||||
}))
|
}))
|
||||||
|
|
||||||
|
vi.mock('@/context/app-context-state', () => ({
|
||||||
|
workspacePermissionKeysAtom: mockAppContextState.workspacePermissionKeysAtom,
|
||||||
|
}))
|
||||||
|
|
||||||
|
vi.mock('jotai', () => ({
|
||||||
|
useAtomValue: (atom: unknown) => {
|
||||||
|
if (atom === mockAppContextState.workspacePermissionKeysAtom)
|
||||||
|
return mockAppContextState.workspacePermissionKeys
|
||||||
|
|
||||||
|
throw new Error('Unexpected atom')
|
||||||
|
},
|
||||||
|
}))
|
||||||
|
|
||||||
// Mock the format time hook
|
// Mock the format time hook
|
||||||
vi.mock('@/hooks/use-format-time-from-now', () => ({
|
vi.mock('@/hooks/use-format-time-from-now', () => ({
|
||||||
useFormatTimeFromNow: () => ({
|
useFormatTimeFromNow: () => ({
|
||||||
@ -155,7 +171,7 @@ describe('MCPCard', () => {
|
|||||||
mockDeleteMCP.mockClear()
|
mockDeleteMCP.mockClear()
|
||||||
mockUpdateMCP.mockResolvedValue({ result: 'success' })
|
mockUpdateMCP.mockResolvedValue({ result: 'success' })
|
||||||
mockDeleteMCP.mockResolvedValue({ result: 'success' })
|
mockDeleteMCP.mockResolvedValue({ result: 'success' })
|
||||||
mockWorkspacePermissionKeys = ['mcp.manage']
|
mockAppContextState.workspacePermissionKeys = ['mcp.manage']
|
||||||
})
|
})
|
||||||
|
|
||||||
describe('Rendering', () => {
|
describe('Rendering', () => {
|
||||||
@ -343,7 +359,7 @@ describe('MCPCard', () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
it('should not render operation dropdown when user lacks mcp.manage', () => {
|
it('should not render operation dropdown when user lacks mcp.manage', () => {
|
||||||
mockWorkspacePermissionKeys = []
|
mockAppContextState.workspacePermissionKeys = []
|
||||||
|
|
||||||
render(<MCPCard {...defaultProps} />, { wrapper: createWrapper() })
|
render(<MCPCard {...defaultProps} />, { wrapper: createWrapper() })
|
||||||
|
|
||||||
|
|||||||
@ -107,16 +107,31 @@ vi.mock('../tool-item', () => ({
|
|||||||
),
|
),
|
||||||
}))
|
}))
|
||||||
|
|
||||||
// Mutable workspace permission state
|
const mockAppContextState = vi.hoisted(() => ({
|
||||||
let mockWorkspacePermissionKeys: string[] = ['mcp.manage']
|
workspacePermissionKeys: ['mcp.manage'] as string[],
|
||||||
|
workspacePermissionKeysAtom: Symbol('workspacePermissionKeysAtom'),
|
||||||
|
}))
|
||||||
|
|
||||||
// Mock the app context
|
// Mock the app context
|
||||||
vi.mock('@/context/app-context', () => ({
|
vi.mock('@/context/app-context', () => ({
|
||||||
useSelector: (selector: (state: { workspacePermissionKeys: string[] }) => unknown) => selector({
|
useSelector: (selector: (state: { workspacePermissionKeys: string[] }) => unknown) => selector({
|
||||||
workspacePermissionKeys: mockWorkspacePermissionKeys,
|
workspacePermissionKeys: mockAppContextState.workspacePermissionKeys,
|
||||||
}),
|
}),
|
||||||
}))
|
}))
|
||||||
|
|
||||||
|
vi.mock('@/context/app-context-state', () => ({
|
||||||
|
workspacePermissionKeysAtom: mockAppContextState.workspacePermissionKeysAtom,
|
||||||
|
}))
|
||||||
|
|
||||||
|
vi.mock('jotai', () => ({
|
||||||
|
useAtomValue: (atom: unknown) => {
|
||||||
|
if (atom === mockAppContextState.workspacePermissionKeysAtom)
|
||||||
|
return mockAppContextState.workspacePermissionKeys
|
||||||
|
|
||||||
|
throw new Error('Unexpected atom')
|
||||||
|
},
|
||||||
|
}))
|
||||||
|
|
||||||
// Mock the plugins service
|
// Mock the plugins service
|
||||||
vi.mock('@/service/use-plugins', () => ({
|
vi.mock('@/service/use-plugins', () => ({
|
||||||
useInstalledPluginList: () => ({
|
useInstalledPluginList: () => ({
|
||||||
@ -195,7 +210,7 @@ describe('MCPDetailContent', () => {
|
|||||||
mockIsFetching = false
|
mockIsFetching = false
|
||||||
mockIsUpdating = false
|
mockIsUpdating = false
|
||||||
mockIsAuthorizing = false
|
mockIsAuthorizing = false
|
||||||
mockWorkspacePermissionKeys = ['mcp.manage']
|
mockAppContextState.workspacePermissionKeys = ['mcp.manage']
|
||||||
})
|
})
|
||||||
|
|
||||||
describe('Rendering', () => {
|
describe('Rendering', () => {
|
||||||
@ -232,7 +247,7 @@ describe('MCPDetailContent', () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
it('should render read-only detail when user lacks mcp.manage', () => {
|
it('should render read-only detail when user lacks mcp.manage', () => {
|
||||||
mockWorkspacePermissionKeys = []
|
mockAppContextState.workspacePermissionKeys = []
|
||||||
|
|
||||||
render(<MCPDetailContent {...defaultProps} />, { wrapper: createWrapper() })
|
render(<MCPDetailContent {...defaultProps} />, { wrapper: createWrapper() })
|
||||||
|
|
||||||
@ -466,7 +481,7 @@ describe('MCPDetailContent', () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
it('should disable authorize action when user lacks mcp.manage', () => {
|
it('should disable authorize action when user lacks mcp.manage', () => {
|
||||||
mockWorkspacePermissionKeys = []
|
mockAppContextState.workspacePermissionKeys = []
|
||||||
const detail = createMockDetail({ is_team_authorization: false })
|
const detail = createMockDetail({ is_team_authorization: false })
|
||||||
render(
|
render(
|
||||||
<MCPDetailContent {...defaultProps} detail={detail} />,
|
<MCPDetailContent {...defaultProps} detail={detail} />,
|
||||||
@ -756,7 +771,7 @@ describe('MCPDetailContent', () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
it('should not run OAuth authorization when user lacks mcp.manage', async () => {
|
it('should not run OAuth authorization when user lacks mcp.manage', async () => {
|
||||||
mockWorkspacePermissionKeys = []
|
mockAppContextState.workspacePermissionKeys = []
|
||||||
mockAuthorizeMcp.mockResolvedValue({ authorization_url: 'https://oauth.example.com' })
|
mockAuthorizeMcp.mockResolvedValue({ authorization_url: 'https://oauth.example.com' })
|
||||||
const detail = createMockDetail({ is_team_authorization: false })
|
const detail = createMockDetail({ is_team_authorization: false })
|
||||||
|
|
||||||
@ -800,7 +815,7 @@ describe('MCPDetailContent', () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
it('should disable authorized button when user lacks mcp.manage', () => {
|
it('should disable authorized button when user lacks mcp.manage', () => {
|
||||||
mockWorkspacePermissionKeys = []
|
mockAppContextState.workspacePermissionKeys = []
|
||||||
const detail = createMockDetail({ is_team_authorization: true })
|
const detail = createMockDetail({ is_team_authorization: true })
|
||||||
render(
|
render(
|
||||||
<MCPDetailContent {...defaultProps} detail={detail} />,
|
<MCPDetailContent {...defaultProps} detail={detail} />,
|
||||||
|
|||||||
@ -2,9 +2,10 @@
|
|||||||
import type { AppDetailResponse } from '@/models/app'
|
import type { AppDetailResponse } from '@/models/app'
|
||||||
import type { AppSSO } from '@/types/app'
|
import type { AppSSO } from '@/types/app'
|
||||||
import { useQuery, useQueryClient } from '@tanstack/react-query'
|
import { useQuery, useQueryClient } from '@tanstack/react-query'
|
||||||
|
import { useAtomValue } from 'jotai'
|
||||||
import { useCallback, useMemo, useState } from 'react'
|
import { useCallback, useMemo, useState } from 'react'
|
||||||
import { BlockEnum } from '@/app/components/workflow/types'
|
import { BlockEnum } from '@/app/components/workflow/types'
|
||||||
import { useSelector as useAppContextWithSelector } from '@/context/app-context'
|
import { userProfileIdAtom, workspacePermissionKeysAtom } from '@/context/app-context-state'
|
||||||
import { fetchAppDetail } from '@/service/apps'
|
import { fetchAppDetail } from '@/service/apps'
|
||||||
import {
|
import {
|
||||||
useInvalidateMCPServerDetail,
|
useInvalidateMCPServerDetail,
|
||||||
@ -36,8 +37,8 @@ export const useMCPServiceCardState = (
|
|||||||
const { mutateAsync: updateMCPServer } = useUpdateMCPServer()
|
const { mutateAsync: updateMCPServer } = useUpdateMCPServer()
|
||||||
const { mutateAsync: refreshMCPServerCode, isPending: genLoading } = useRefreshMCPServerCode()
|
const { mutateAsync: refreshMCPServerCode, isPending: genLoading } = useRefreshMCPServerCode()
|
||||||
const invalidateMCPServerDetail = useInvalidateMCPServerDetail()
|
const invalidateMCPServerDetail = useInvalidateMCPServerDetail()
|
||||||
const currentUserId = useAppContextWithSelector(state => state.userProfile?.id)
|
const currentUserId = useAtomValue(userProfileIdAtom)
|
||||||
const workspacePermissionKeys = useAppContextWithSelector(state => state.workspacePermissionKeys)
|
const workspacePermissionKeys = useAtomValue(workspacePermissionKeysAtom)
|
||||||
|
|
||||||
const canManageMCP = useMemo(
|
const canManageMCP = useMemo(
|
||||||
() => getAppACLCapabilities(appInfo.permission_keys, {
|
() => getAppACLCapabilities(appInfo.permission_keys, {
|
||||||
|
|||||||
@ -4,14 +4,30 @@ import { beforeEach, describe, expect, it, vi } from 'vitest'
|
|||||||
import { AuthType } from '../../types'
|
import { AuthType } from '../../types'
|
||||||
import CustomCreateCard, { NewCustomToolButton } from '../custom-create-card'
|
import CustomCreateCard, { NewCustomToolButton } from '../custom-create-card'
|
||||||
|
|
||||||
let mockWorkspacePermissionKeys: string[] = ['tool.manage']
|
const mockAppContextState = vi.hoisted(() => ({
|
||||||
|
workspacePermissionKeys: ['tool.manage'] as string[],
|
||||||
|
workspacePermissionKeysAtom: Symbol('workspacePermissionKeysAtom'),
|
||||||
|
}))
|
||||||
|
|
||||||
vi.mock('@/context/app-context', () => ({
|
vi.mock('@/context/app-context', () => ({
|
||||||
useSelector: <T,>(selector: (state: { workspacePermissionKeys: string[] }) => T): T => selector({
|
useSelector: <T,>(selector: (state: { workspacePermissionKeys: string[] }) => T): T => selector({
|
||||||
workspacePermissionKeys: mockWorkspacePermissionKeys,
|
workspacePermissionKeys: mockAppContextState.workspacePermissionKeys,
|
||||||
}),
|
}),
|
||||||
}))
|
}))
|
||||||
|
|
||||||
|
vi.mock('@/context/app-context-state', () => ({
|
||||||
|
workspacePermissionKeysAtom: mockAppContextState.workspacePermissionKeysAtom,
|
||||||
|
}))
|
||||||
|
|
||||||
|
vi.mock('jotai', () => ({
|
||||||
|
useAtomValue: (atom: unknown) => {
|
||||||
|
if (atom === mockAppContextState.workspacePermissionKeysAtom)
|
||||||
|
return mockAppContextState.workspacePermissionKeys
|
||||||
|
|
||||||
|
throw new Error('Unexpected atom')
|
||||||
|
},
|
||||||
|
}))
|
||||||
|
|
||||||
// Mock useLocale and useDocLink
|
// Mock useLocale and useDocLink
|
||||||
vi.mock('@/context/i18n', () => ({
|
vi.mock('@/context/i18n', () => ({
|
||||||
useLocale: () => 'en-US',
|
useLocale: () => 'en-US',
|
||||||
@ -81,7 +97,7 @@ describe('CustomCreateCard', () => {
|
|||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
vi.clearAllMocks()
|
vi.clearAllMocks()
|
||||||
mockWorkspacePermissionKeys = ['tool.manage']
|
mockAppContextState.workspacePermissionKeys = ['tool.manage']
|
||||||
mockModalVisible = false
|
mockModalVisible = false
|
||||||
mockCreateCustomCollection.mockResolvedValue({})
|
mockCreateCustomCollection.mockResolvedValue({})
|
||||||
})
|
})
|
||||||
@ -94,7 +110,7 @@ describe('CustomCreateCard', () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
it('should not render anything when user does not have tool.manage', () => {
|
it('should not render anything when user does not have tool.manage', () => {
|
||||||
mockWorkspacePermissionKeys = []
|
mockAppContextState.workspacePermissionKeys = []
|
||||||
|
|
||||||
const { container } = render(<CustomCreateCard onRefreshData={mockOnRefreshData} />)
|
const { container } = render(<CustomCreateCard onRefreshData={mockOnRefreshData} />)
|
||||||
|
|
||||||
@ -146,7 +162,7 @@ describe('CustomCreateCard', () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
it('should not render toolbar add button when user does not have tool.manage', () => {
|
it('should not render toolbar add button when user does not have tool.manage', () => {
|
||||||
mockWorkspacePermissionKeys = []
|
mockAppContextState.workspacePermissionKeys = []
|
||||||
|
|
||||||
const { container } = render(<NewCustomToolButton onRefreshData={mockOnRefreshData} />)
|
const { container } = render(<NewCustomToolButton onRefreshData={mockOnRefreshData} />)
|
||||||
|
|
||||||
|
|||||||
@ -15,6 +15,7 @@ vi.mock('@/i18n-config/language', () => ({
|
|||||||
const mockIsCurrentWorkspaceManager = vi.fn(() => true)
|
const mockIsCurrentWorkspaceManager = vi.fn(() => true)
|
||||||
const mockAppContextState = vi.hoisted(() => ({
|
const mockAppContextState = vi.hoisted(() => ({
|
||||||
workspacePermissionKeys: ['tool.manage', 'credential.use', 'credential.create', 'credential.manage'] as string[],
|
workspacePermissionKeys: ['tool.manage', 'credential.use', 'credential.create', 'credential.manage'] as string[],
|
||||||
|
workspacePermissionKeysAtom: Symbol('workspacePermissionKeysAtom'),
|
||||||
}))
|
}))
|
||||||
vi.mock('@/context/app-context', () => ({
|
vi.mock('@/context/app-context', () => ({
|
||||||
useAppContext: () => ({
|
useAppContext: () => ({
|
||||||
@ -25,6 +26,19 @@ vi.mock('@/context/app-context', () => ({
|
|||||||
}),
|
}),
|
||||||
}))
|
}))
|
||||||
|
|
||||||
|
vi.mock('@/context/app-context-state', () => ({
|
||||||
|
workspacePermissionKeysAtom: mockAppContextState.workspacePermissionKeysAtom,
|
||||||
|
}))
|
||||||
|
|
||||||
|
vi.mock('jotai', () => ({
|
||||||
|
useAtomValue: (atom: unknown) => {
|
||||||
|
if (atom === mockAppContextState.workspacePermissionKeysAtom)
|
||||||
|
return mockAppContextState.workspacePermissionKeys
|
||||||
|
|
||||||
|
throw new Error('Unexpected atom')
|
||||||
|
},
|
||||||
|
}))
|
||||||
|
|
||||||
const mockSetShowModelModal = vi.fn()
|
const mockSetShowModelModal = vi.fn()
|
||||||
vi.mock('@/context/modal-context', () => ({
|
vi.mock('@/context/modal-context', () => ({
|
||||||
useModalContext: () => ({
|
useModalContext: () => ({
|
||||||
|
|||||||
@ -69,6 +69,18 @@ vi.mock('@/context/app-context', () => ({
|
|||||||
}),
|
}),
|
||||||
}))
|
}))
|
||||||
|
|
||||||
|
vi.mock('@/context/app-context-state', async (importOriginal) => {
|
||||||
|
const { createAppContextStateAtomMock } = await import('@/__tests__/utils/mock-app-context-state')
|
||||||
|
return createAppContextStateAtomMock(importOriginal, () => ({
|
||||||
|
workspacePermissionKeys: mockWorkspacePermissionKeys,
|
||||||
|
}))
|
||||||
|
})
|
||||||
|
|
||||||
|
vi.mock('jotai', async (importOriginal) => {
|
||||||
|
const { createAppContextStateJotaiMock } = await import('@/__tests__/utils/mock-app-context-state')
|
||||||
|
return createAppContextStateJotaiMock(importOriginal)
|
||||||
|
})
|
||||||
|
|
||||||
vi.mock('@/hooks/use-theme', () => ({
|
vi.mock('@/hooks/use-theme', () => ({
|
||||||
default: vi.fn(),
|
default: vi.fn(),
|
||||||
}))
|
}))
|
||||||
|
|||||||
@ -21,6 +21,18 @@ vi.mock('@/context/app-context', () => ({
|
|||||||
}),
|
}),
|
||||||
}))
|
}))
|
||||||
|
|
||||||
|
vi.mock('@/context/app-context-state', async (importOriginal) => {
|
||||||
|
const { createAppContextStateAtomMock } = await import('@/__tests__/utils/mock-app-context-state')
|
||||||
|
return createAppContextStateAtomMock(importOriginal, () => ({
|
||||||
|
workspacePermissionKeys: mockWorkspacePermissionKeys,
|
||||||
|
}))
|
||||||
|
})
|
||||||
|
|
||||||
|
vi.mock('jotai', async (importOriginal) => {
|
||||||
|
const { createAppContextStateJotaiMock } = await import('@/__tests__/utils/mock-app-context-state')
|
||||||
|
return createAppContextStateJotaiMock(importOriginal)
|
||||||
|
})
|
||||||
|
|
||||||
vi.mock('@/service/use-tools', () => ({
|
vi.mock('@/service/use-tools', () => ({
|
||||||
useAllBuiltInTools: (enabled: boolean) => mockBuiltInTools(enabled),
|
useAllBuiltInTools: (enabled: boolean) => mockBuiltInTools(enabled),
|
||||||
useAllCustomTools: (enabled: boolean) => mockCustomTools(enabled),
|
useAllCustomTools: (enabled: boolean) => mockCustomTools(enabled),
|
||||||
|
|||||||
@ -1,13 +1,19 @@
|
|||||||
import { renderHook } from '@testing-library/react'
|
import { renderHook } from '@testing-library/react'
|
||||||
import { useCredentialPermissions } from './use-credential-permissions'
|
import { useCredentialPermissions } from './use-credential-permissions'
|
||||||
|
|
||||||
let mockWorkspacePermissionKeys: string[] | null = []
|
let mockWorkspacePermissionKeys: string[] = []
|
||||||
|
|
||||||
vi.mock('@/context/app-context', () => ({
|
vi.mock('@/context/app-context-state', async (importOriginal) => {
|
||||||
useSelector: (selector: (state: { workspacePermissionKeys: string[] | null }) => unknown) => selector({
|
const { createAppContextStateAtomMock } = await import('@/__tests__/utils/mock-app-context-state')
|
||||||
|
return createAppContextStateAtomMock(importOriginal, () => ({
|
||||||
workspacePermissionKeys: mockWorkspacePermissionKeys,
|
workspacePermissionKeys: mockWorkspacePermissionKeys,
|
||||||
}),
|
}))
|
||||||
}))
|
})
|
||||||
|
|
||||||
|
vi.mock('jotai', async (importOriginal) => {
|
||||||
|
const { createAppContextStateJotaiMock } = await import('@/__tests__/utils/mock-app-context-state')
|
||||||
|
return createAppContextStateJotaiMock(importOriginal)
|
||||||
|
})
|
||||||
|
|
||||||
describe('useCredentialPermissions', () => {
|
describe('useCredentialPermissions', () => {
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
@ -39,8 +45,8 @@ describe('useCredentialPermissions', () => {
|
|||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
it('should handle missing workspace permissions as no credential capabilities', () => {
|
it('should handle empty workspace permissions as no credential capabilities', () => {
|
||||||
mockWorkspacePermissionKeys = null
|
mockWorkspacePermissionKeys = []
|
||||||
|
|
||||||
const { result } = renderHook(() => useCredentialPermissions())
|
const { result } = renderHook(() => useCredentialPermissions())
|
||||||
|
|
||||||
|
|||||||
@ -1,8 +1,9 @@
|
|||||||
import { useSelector as useAppContextSelector } from '@/context/app-context'
|
import { useAtomValue } from 'jotai'
|
||||||
|
import { workspacePermissionKeysAtom } from '@/context/app-context-state'
|
||||||
import { hasPermission } from '@/utils/permission'
|
import { hasPermission } from '@/utils/permission'
|
||||||
|
|
||||||
export const useCredentialPermissions = () => {
|
export const useCredentialPermissions = () => {
|
||||||
const workspacePermissionKeys = useAppContextSelector(state => state.workspacePermissionKeys)
|
const workspacePermissionKeys = useAtomValue(workspacePermissionKeysAtom)
|
||||||
|
|
||||||
return {
|
return {
|
||||||
canUseCredential: hasPermission(workspacePermissionKeys, 'credential.use'),
|
canUseCredential: hasPermission(workspacePermissionKeys, 'credential.use'),
|
||||||
|
|||||||
@ -19,9 +19,11 @@ import {
|
|||||||
const {
|
const {
|
||||||
mockGet,
|
mockGet,
|
||||||
mockPost,
|
mockPost,
|
||||||
|
mockWorkspacePermissionKeysAtom,
|
||||||
} = vi.hoisted(() => ({
|
} = vi.hoisted(() => ({
|
||||||
mockGet: vi.fn(),
|
mockGet: vi.fn(),
|
||||||
mockPost: vi.fn(),
|
mockPost: vi.fn(),
|
||||||
|
mockWorkspacePermissionKeysAtom: Symbol('workspacePermissionKeysAtom'),
|
||||||
}))
|
}))
|
||||||
|
|
||||||
vi.mock('@/service/base', () => ({
|
vi.mock('@/service/base', () => ({
|
||||||
@ -37,12 +39,17 @@ vi.mock('@/app/components/plugins/install-plugin/hooks/use-refresh-plugin-list',
|
|||||||
}),
|
}),
|
||||||
}))
|
}))
|
||||||
|
|
||||||
vi.mock('@/context/app-context', () => ({
|
vi.mock('@/context/app-context-state', () => ({
|
||||||
useAppContext: () => ({
|
workspacePermissionKeysAtom: mockWorkspacePermissionKeysAtom,
|
||||||
isCurrentWorkspaceManager: true,
|
}))
|
||||||
isCurrentWorkspaceOwner: false,
|
|
||||||
workspacePermissionKeys: ['plugin.install'],
|
vi.mock('jotai', () => ({
|
||||||
}),
|
useAtomValue: (atom: unknown) => {
|
||||||
|
if (atom === mockWorkspacePermissionKeysAtom)
|
||||||
|
return ['plugin.install']
|
||||||
|
|
||||||
|
throw new Error('Unexpected atom')
|
||||||
|
},
|
||||||
}))
|
}))
|
||||||
|
|
||||||
vi.mock('../use-tools', () => ({
|
vi.mock('../use-tools', () => ({
|
||||||
|
|||||||
@ -40,12 +40,13 @@ import {
|
|||||||
useQueryClient,
|
useQueryClient,
|
||||||
} from '@tanstack/react-query'
|
} from '@tanstack/react-query'
|
||||||
import { cloneDeep } from 'es-toolkit/object'
|
import { cloneDeep } from 'es-toolkit/object'
|
||||||
|
import { useAtomValue } from 'jotai'
|
||||||
import { useCallback, useEffect, useRef } from 'react'
|
import { useCallback, useEffect, useRef } from 'react'
|
||||||
import { FormTypeEnum } from '@/app/components/base/form/types'
|
import { FormTypeEnum } from '@/app/components/base/form/types'
|
||||||
import useRefreshPluginList from '@/app/components/plugins/install-plugin/hooks/use-refresh-plugin-list'
|
import useRefreshPluginList from '@/app/components/plugins/install-plugin/hooks/use-refresh-plugin-list'
|
||||||
import { getFormattedPlugin } from '@/app/components/plugins/marketplace/utils'
|
import { getFormattedPlugin } from '@/app/components/plugins/marketplace/utils'
|
||||||
import { PluginCategoryEnum, PluginSource, TaskStatus } from '@/app/components/plugins/types'
|
import { PluginCategoryEnum, PluginSource, TaskStatus } from '@/app/components/plugins/types'
|
||||||
import { useAppContext } from '@/context/app-context'
|
import { workspacePermissionKeysAtom } from '@/context/app-context-state'
|
||||||
import { fetchModelProviderModelList } from '@/service/common'
|
import { fetchModelProviderModelList } from '@/service/common'
|
||||||
import { fetchPluginInfoFromMarketPlace, uninstallPlugin } from '@/service/plugins'
|
import { fetchPluginInfoFromMarketPlace, uninstallPlugin } from '@/service/plugins'
|
||||||
import { hasPermission } from '@/utils/permission'
|
import { hasPermission } from '@/utils/permission'
|
||||||
@ -1233,7 +1234,7 @@ export const useFetchPluginsInMarketPlaceByInfo = (infos: MarketplacePluginInfoR
|
|||||||
export const usePluginTaskList = (category?: PluginCategoryEnum | string) => {
|
export const usePluginTaskList = (category?: PluginCategoryEnum | string) => {
|
||||||
const initializedRef = useRef(false)
|
const initializedRef = useRef(false)
|
||||||
const queryClient = useQueryClient()
|
const queryClient = useQueryClient()
|
||||||
const { workspacePermissionKeys } = useAppContext()
|
const workspacePermissionKeys = useAtomValue(workspacePermissionKeysAtom)
|
||||||
const canManagement = hasPermission(workspacePermissionKeys, 'plugin.install')
|
const canManagement = hasPermission(workspacePermissionKeys, 'plugin.install')
|
||||||
const { refreshPluginList } = useRefreshPluginList()
|
const { refreshPluginList } = useRefreshPluginList()
|
||||||
const query = useQuery<PluginTaskListResponse>({
|
const query = useQuery<PluginTaskListResponse>({
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user