mirror of https://github.com/langgenius/dify.git
Merge branch 'main' into 3-23-update-ci
This commit is contained in:
commit
36bf891b5b
|
|
@ -224,6 +224,20 @@ describe('DocumentSettings', () => {
|
||||||
|
|
||||||
// Data source types
|
// Data source types
|
||||||
describe('Data Source Types', () => {
|
describe('Data Source Types', () => {
|
||||||
|
it('should handle upload_file_id data source format', () => {
|
||||||
|
mockDocumentDetail = {
|
||||||
|
name: 'test-document',
|
||||||
|
data_source_type: 'upload_file',
|
||||||
|
data_source_info: {
|
||||||
|
upload_file_id: '4a807f05-45d6-4fc4-b7a8-b009a4568b36',
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
render(<DocumentSettings {...defaultProps} />)
|
||||||
|
|
||||||
|
expect(screen.getByTestId('files-count')).toHaveTextContent('1')
|
||||||
|
})
|
||||||
|
|
||||||
it('should handle legacy upload_file data source', () => {
|
it('should handle legacy upload_file data source', () => {
|
||||||
mockDocumentDetail = {
|
mockDocumentDetail = {
|
||||||
name: 'test-document',
|
name: 'test-document',
|
||||||
|
|
@ -307,6 +321,18 @@ describe('DocumentSettings', () => {
|
||||||
expect(screen.getByTestId('files-count')).toHaveTextContent('0')
|
expect(screen.getByTestId('files-count')).toHaveTextContent('0')
|
||||||
})
|
})
|
||||||
|
|
||||||
|
it('should handle empty data_source_info object', () => {
|
||||||
|
mockDocumentDetail = {
|
||||||
|
name: 'test-document',
|
||||||
|
data_source_type: 'upload_file',
|
||||||
|
data_source_info: {},
|
||||||
|
}
|
||||||
|
|
||||||
|
render(<DocumentSettings {...defaultProps} />)
|
||||||
|
|
||||||
|
expect(screen.getByTestId('files-count')).toHaveTextContent('0')
|
||||||
|
})
|
||||||
|
|
||||||
it('should maintain structure when rerendered', () => {
|
it('should maintain structure when rerendered', () => {
|
||||||
const { rerender } = render(
|
const { rerender } = render(
|
||||||
<DocumentSettings datasetId="dataset-1" documentId="doc-1" />,
|
<DocumentSettings datasetId="dataset-1" documentId="doc-1" />,
|
||||||
|
|
@ -317,4 +343,37 @@ describe('DocumentSettings', () => {
|
||||||
expect(screen.getByTestId('step-two')).toBeInTheDocument()
|
expect(screen.getByTestId('step-two')).toBeInTheDocument()
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
describe('Files Extraction Regression Tests', () => {
|
||||||
|
it('should correctly extract file ID from upload_file_id format', () => {
|
||||||
|
const fileId = '4a807f05-45d6-4fc4-b7a8-b009a4568b36'
|
||||||
|
mockDocumentDetail = {
|
||||||
|
name: 'test-document.pdf',
|
||||||
|
data_source_type: 'upload_file',
|
||||||
|
data_source_info: {
|
||||||
|
upload_file_id: fileId,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
render(<DocumentSettings {...defaultProps} />)
|
||||||
|
|
||||||
|
// Verify files array is populated with correct file ID
|
||||||
|
expect(screen.getByTestId('files-count')).toHaveTextContent('1')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should preserve document name when using upload_file_id format', () => {
|
||||||
|
const documentName = 'my-uploaded-document.txt'
|
||||||
|
mockDocumentDetail = {
|
||||||
|
name: documentName,
|
||||||
|
data_source_type: 'upload_file',
|
||||||
|
data_source_info: {
|
||||||
|
upload_file_id: 'some-file-id',
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
render(<DocumentSettings {...defaultProps} />)
|
||||||
|
|
||||||
|
expect(screen.getByTestId('files-count')).toHaveTextContent('1')
|
||||||
|
})
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|
|
||||||
|
|
@ -8,6 +8,7 @@ import type {
|
||||||
LegacyDataSourceInfo,
|
LegacyDataSourceInfo,
|
||||||
LocalFileInfo,
|
LocalFileInfo,
|
||||||
OnlineDocumentInfo,
|
OnlineDocumentInfo,
|
||||||
|
UploadFileIdInfo,
|
||||||
WebsiteCrawlInfo,
|
WebsiteCrawlInfo,
|
||||||
} from '@/models/datasets'
|
} from '@/models/datasets'
|
||||||
import { useBoolean } from 'ahooks'
|
import { useBoolean } from 'ahooks'
|
||||||
|
|
@ -61,6 +62,7 @@ const DocumentSettings = ({ datasetId, documentId }: DocumentSettingsProps) => {
|
||||||
|
|
||||||
const dataSourceInfo = documentDetail?.data_source_info
|
const dataSourceInfo = documentDetail?.data_source_info
|
||||||
|
|
||||||
|
// Type guards for DataSourceInfo union
|
||||||
const isLegacyDataSourceInfo = (info: DataSourceInfo | undefined): info is LegacyDataSourceInfo => {
|
const isLegacyDataSourceInfo = (info: DataSourceInfo | undefined): info is LegacyDataSourceInfo => {
|
||||||
return !!info && 'upload_file' in info
|
return !!info && 'upload_file' in info
|
||||||
}
|
}
|
||||||
|
|
@ -73,10 +75,15 @@ const DocumentSettings = ({ datasetId, documentId }: DocumentSettingsProps) => {
|
||||||
const isLocalFileInfo = (info: DataSourceInfo | undefined): info is LocalFileInfo => {
|
const isLocalFileInfo = (info: DataSourceInfo | undefined): info is LocalFileInfo => {
|
||||||
return !!info && 'related_id' in info && 'transfer_method' in info
|
return !!info && 'related_id' in info && 'transfer_method' in info
|
||||||
}
|
}
|
||||||
|
const isUploadFileIdInfo = (info: DataSourceInfo | undefined): info is UploadFileIdInfo => {
|
||||||
|
return !!info && 'upload_file_id' in info
|
||||||
|
}
|
||||||
|
|
||||||
const legacyInfo = isLegacyDataSourceInfo(dataSourceInfo) ? dataSourceInfo : undefined
|
const legacyInfo = isLegacyDataSourceInfo(dataSourceInfo) ? dataSourceInfo : undefined
|
||||||
const websiteInfo = isWebsiteCrawlInfo(dataSourceInfo) ? dataSourceInfo : undefined
|
const websiteInfo = isWebsiteCrawlInfo(dataSourceInfo) ? dataSourceInfo : undefined
|
||||||
const onlineDocumentInfo = isOnlineDocumentInfo(dataSourceInfo) ? dataSourceInfo : undefined
|
const onlineDocumentInfo = isOnlineDocumentInfo(dataSourceInfo) ? dataSourceInfo : undefined
|
||||||
const localFileInfo = isLocalFileInfo(dataSourceInfo) ? dataSourceInfo : undefined
|
const localFileInfo = isLocalFileInfo(dataSourceInfo) ? dataSourceInfo : undefined
|
||||||
|
const uploadFileIdInfo = isUploadFileIdInfo(dataSourceInfo) ? dataSourceInfo : undefined
|
||||||
|
|
||||||
const currentPage = useMemo(() => {
|
const currentPage = useMemo(() => {
|
||||||
if (legacyInfo) {
|
if (legacyInfo) {
|
||||||
|
|
@ -101,8 +108,20 @@ const DocumentSettings = ({ datasetId, documentId }: DocumentSettingsProps) => {
|
||||||
}, [documentDetail?.data_source_type, documentDetail?.name, legacyInfo, onlineDocumentInfo])
|
}, [documentDetail?.data_source_type, documentDetail?.name, legacyInfo, onlineDocumentInfo])
|
||||||
|
|
||||||
const files = useMemo<CustomFile[]>(() => {
|
const files = useMemo<CustomFile[]>(() => {
|
||||||
if (legacyInfo?.upload_file)
|
// Handle upload_file_id format
|
||||||
return [legacyInfo.upload_file as CustomFile]
|
if (uploadFileIdInfo) {
|
||||||
|
return [{
|
||||||
|
id: uploadFileIdInfo.upload_file_id,
|
||||||
|
name: documentDetail?.name || '',
|
||||||
|
} as unknown as CustomFile]
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle legacy upload_file format
|
||||||
|
if (legacyInfo?.upload_file) {
|
||||||
|
return [legacyInfo.upload_file as unknown as CustomFile]
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle local file info format
|
||||||
if (localFileInfo) {
|
if (localFileInfo) {
|
||||||
const { related_id, name, extension } = localFileInfo
|
const { related_id, name, extension } = localFileInfo
|
||||||
return [{
|
return [{
|
||||||
|
|
@ -111,8 +130,9 @@ const DocumentSettings = ({ datasetId, documentId }: DocumentSettingsProps) => {
|
||||||
extension,
|
extension,
|
||||||
} as unknown as CustomFile]
|
} as unknown as CustomFile]
|
||||||
}
|
}
|
||||||
|
|
||||||
return []
|
return []
|
||||||
}, [legacyInfo?.upload_file, localFileInfo])
|
}, [uploadFileIdInfo, legacyInfo?.upload_file, localFileInfo, documentDetail?.name])
|
||||||
|
|
||||||
const websitePages = useMemo(() => {
|
const websitePages = useMemo(() => {
|
||||||
if (!websiteInfo)
|
if (!websiteInfo)
|
||||||
|
|
|
||||||
|
|
@ -315,14 +315,14 @@ describe('AccountSetting', () => {
|
||||||
it('should handle scroll event in panel', () => {
|
it('should handle scroll event in panel', () => {
|
||||||
// Act
|
// Act
|
||||||
renderAccountSetting()
|
renderAccountSetting()
|
||||||
const scrollContainer = screen.getByRole('dialog').querySelector('.overflow-y-auto')
|
const scrollContainer = screen.getByRole('dialog').querySelector('.overscroll-contain')
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
expect(scrollContainer).toBeInTheDocument()
|
expect(scrollContainer).toBeInTheDocument()
|
||||||
if (scrollContainer) {
|
if (scrollContainer) {
|
||||||
// Scroll down
|
// Scroll down
|
||||||
fireEvent.scroll(scrollContainer, { target: { scrollTop: 100 } })
|
fireEvent.scroll(scrollContainer, { target: { scrollTop: 100 } })
|
||||||
expect(scrollContainer).toHaveClass('overflow-y-auto')
|
expect(scrollContainer).toHaveClass('overscroll-contain')
|
||||||
|
|
||||||
// Scroll back up
|
// Scroll back up
|
||||||
fireEvent.scroll(scrollContainer, { target: { scrollTop: 0 } })
|
fireEvent.scroll(scrollContainer, { target: { scrollTop: 0 } })
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,9 @@
|
||||||
'use client'
|
'use client'
|
||||||
import type { AccountSettingTab } from '@/app/components/header/account-setting/constants'
|
import type { AccountSettingTab } from '@/app/components/header/account-setting/constants'
|
||||||
import { useCallback, useEffect, useRef, useState } from 'react'
|
import { useCallback, useState } from 'react'
|
||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from 'react-i18next'
|
||||||
import SearchInput from '@/app/components/base/search-input'
|
import SearchInput from '@/app/components/base/search-input'
|
||||||
|
import { ScrollArea } from '@/app/components/base/ui/scroll-area'
|
||||||
import BillingPage from '@/app/components/billing/billing-page'
|
import BillingPage from '@/app/components/billing/billing-page'
|
||||||
import CustomPage from '@/app/components/custom/custom-page'
|
import CustomPage from '@/app/components/custom/custom-page'
|
||||||
import {
|
import {
|
||||||
|
|
@ -129,20 +130,6 @@ export default function AccountSetting({
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
const scrollRef = useRef<HTMLDivElement>(null)
|
|
||||||
const [scrolled, setScrolled] = useState(false)
|
|
||||||
useEffect(() => {
|
|
||||||
const targetElement = scrollRef.current
|
|
||||||
const scrollHandle = (e: Event) => {
|
|
||||||
const userScrolled = (e.target as HTMLDivElement).scrollTop > 0
|
|
||||||
setScrolled(userScrolled)
|
|
||||||
}
|
|
||||||
targetElement?.addEventListener('scroll', scrollHandle)
|
|
||||||
return () => {
|
|
||||||
targetElement?.removeEventListener('scroll', scrollHandle)
|
|
||||||
}
|
|
||||||
}, [])
|
|
||||||
|
|
||||||
const activeItem = [...menuItems[0].items, ...menuItems[1].items].find(item => item.key === activeMenu)
|
const activeItem = [...menuItems[0].items, ...menuItems[1].items].find(item => item.key === activeMenu)
|
||||||
|
|
||||||
const [searchValue, setSearchValue] = useState<string>('')
|
const [searchValue, setSearchValue] = useState<string>('')
|
||||||
|
|
@ -201,7 +188,7 @@ export default function AccountSetting({
|
||||||
}
|
}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="relative flex w-[824px]">
|
<div className="relative flex min-h-0 w-[824px]">
|
||||||
<div className="fixed right-6 top-6 z-[9999] flex flex-col items-center">
|
<div className="fixed right-6 top-6 z-[9999] flex flex-col items-center">
|
||||||
<Button
|
<Button
|
||||||
variant="tertiary"
|
variant="tertiary"
|
||||||
|
|
@ -214,8 +201,14 @@ export default function AccountSetting({
|
||||||
</Button>
|
</Button>
|
||||||
<div className="mt-1 text-text-tertiary system-2xs-medium-uppercase">ESC</div>
|
<div className="mt-1 text-text-tertiary system-2xs-medium-uppercase">ESC</div>
|
||||||
</div>
|
</div>
|
||||||
<div ref={scrollRef} className="w-full overflow-y-auto bg-components-panel-bg pb-4">
|
<ScrollArea
|
||||||
<div className={cn('sticky top-0 z-20 mx-8 mb-[18px] flex items-center bg-components-panel-bg pb-2 pt-[27px]', scrolled && 'border-b border-divider-regular')}>
|
className="h-full min-h-0 flex-1 bg-components-panel-bg"
|
||||||
|
slotClassNames={{
|
||||||
|
viewport: 'overscroll-contain',
|
||||||
|
content: 'min-h-full pb-4',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<div className="sticky top-0 z-20 mx-8 mb-[18px] flex items-center bg-components-panel-bg pb-2 pt-[27px]">
|
||||||
<div className="shrink-0 text-text-primary title-2xl-semi-bold">
|
<div className="shrink-0 text-text-primary title-2xl-semi-bold">
|
||||||
{activeItem?.name}
|
{activeItem?.name}
|
||||||
{activeItem?.description && (
|
{activeItem?.description && (
|
||||||
|
|
@ -241,7 +234,7 @@ export default function AccountSetting({
|
||||||
{activeMenu === ACCOUNT_SETTING_TAB.CUSTOM && <CustomPage />}
|
{activeMenu === ACCOUNT_SETTING_TAB.CUSTOM && <CustomPage />}
|
||||||
{activeMenu === ACCOUNT_SETTING_TAB.LANGUAGE && <LanguagePage />}
|
{activeMenu === ACCOUNT_SETTING_TAB.LANGUAGE && <LanguagePage />}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</ScrollArea>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</MenuDialog>
|
</MenuDialog>
|
||||||
|
|
|
||||||
|
|
@ -381,7 +381,11 @@ export type OnlineDriveInfo = {
|
||||||
type: 'file' | 'folder'
|
type: 'file' | 'folder'
|
||||||
}
|
}
|
||||||
|
|
||||||
export type DataSourceInfo = LegacyDataSourceInfo | LocalFileInfo | OnlineDocumentInfo | WebsiteCrawlInfo
|
export type UploadFileIdInfo = {
|
||||||
|
upload_file_id: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export type DataSourceInfo = LegacyDataSourceInfo | LocalFileInfo | OnlineDocumentInfo | WebsiteCrawlInfo | UploadFileIdInfo
|
||||||
|
|
||||||
export type InitialDocumentDetail = {
|
export type InitialDocumentDetail = {
|
||||||
id: string
|
id: string
|
||||||
|
|
|
||||||
|
|
@ -91,11 +91,15 @@ const getFileIndexingEstimateParamsForFile = ({
|
||||||
processRule,
|
processRule,
|
||||||
dataset_id,
|
dataset_id,
|
||||||
}: GetFileIndexingEstimateParamsOptionFile): IndexingEstimateParams => {
|
}: GetFileIndexingEstimateParamsOptionFile): IndexingEstimateParams => {
|
||||||
|
const fileIds = files
|
||||||
|
.map(file => file.id)
|
||||||
|
.filter((id): id is string => Boolean(id))
|
||||||
|
|
||||||
return {
|
return {
|
||||||
info_list: {
|
info_list: {
|
||||||
data_source_type: dataSourceType,
|
data_source_type: dataSourceType,
|
||||||
file_info_list: {
|
file_info_list: {
|
||||||
file_ids: files.map(file => file.id) as string[],
|
file_ids: fileIds,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
indexing_technique: indexingTechnique,
|
indexing_technique: indexingTechnique,
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue