Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

170 - Integration files total download size #192

Merged
merged 10 commits into from
Oct 26, 2023
8 changes: 4 additions & 4 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
},
"dependencies": {
"@faker-js/faker": "7.6.0",
"@iqss/dataverse-client-javascript": "2.0.0-pr88.9d7ced6",
"@iqss/dataverse-client-javascript": "2.0.0-pr92.3fbf381",
"@iqss/dataverse-design-system": "*",
"@istanbuljs/nyc-config-typescript": "1.0.2",
"@tanstack/react-table": "8.9.2",
Expand Down
4 changes: 4 additions & 0 deletions src/files/domain/repositories/FileRepository.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,5 +16,9 @@ export interface FileRepository {
datasetPersistentId: string,
datasetVersion: DatasetVersion
) => Promise<FilesCountInfo>
getFilesTotalDownloadSizeByDatasetPersistentId: (
datasetPersistentId: string,
datasetVersion: DatasetVersion
) => Promise<number>
getUserPermissionsById: (id: number) => Promise<FileUserPermissions>
}
14 changes: 14 additions & 0 deletions src/files/domain/useCases/getFilesTotalDownloadSize.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import { FileRepository } from '../repositories/FileRepository'
import { DatasetVersion } from '../../../dataset/domain/models/Dataset'

export async function getFilesTotalDownloadSize(
fileRepository: FileRepository,
datasetPersistentId: string,
datasetVersion: DatasetVersion
): Promise<number> {
return fileRepository
.getFilesTotalDownloadSizeByDatasetPersistentId(datasetPersistentId, datasetVersion)
.catch((error: Error) => {
throw new Error(error.message)
})
}
18 changes: 17 additions & 1 deletion src/files/infrastructure/FileJSDataverseRepository.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,10 @@ import { FilesCountInfo } from '../domain/models/FilesCountInfo'
import { FilePaginationInfo } from '../domain/models/FilePaginationInfo'
import { FileUserPermissions } from '../domain/models/FileUserPermissions'
import {
FileDownloadSizeMode,
getDatasetFileCounts,
getDatasetFiles,
getDatasetFilesTotalDownloadSize,
getFileDownloadCount,
getFileUserPermissions,
WriteError
Expand All @@ -15,6 +17,8 @@ import { DomainFileMapper } from './mappers/DomainFileMapper'
import { JSFileMapper } from './mappers/JSFileMapper'
import { DatasetVersion } from '../../dataset/domain/models/Dataset'

const includeDeaccessioned = true

export class FileJSDataverseRepository implements FileRepository {
getAllByDatasetPersistentId(
datasetPersistentId: string,
Expand All @@ -28,6 +32,7 @@ export class FileJSDataverseRepository implements FileRepository {
.execute(
datasetPersistentId,
datasetVersion.toString(),
includeDeaccessioned,
jsPagination.limit,
jsPagination.offset,
DomainFileMapper.toJSFileCriteria(criteria)
Expand Down Expand Up @@ -68,7 +73,7 @@ export class FileJSDataverseRepository implements FileRepository {
): Promise<FilesCountInfo> {
// TODO - Take into account the FileCriteria https://github.com/IQSS/dataverse-frontend/issues/172
return getDatasetFileCounts
.execute(datasetPersistentId, datasetVersion.toString())
.execute(datasetPersistentId, datasetVersion.toString(), includeDeaccessioned)
.then((jsFilesCountInfo) => {
return JSFileMapper.toFilesCountInfo(jsFilesCountInfo)
})
Expand All @@ -77,6 +82,17 @@ export class FileJSDataverseRepository implements FileRepository {
})
}

getFilesTotalDownloadSizeByDatasetPersistentId(
datasetPersistentId: string,
datasetVersion: DatasetVersion
): Promise<number> {
return getDatasetFilesTotalDownloadSize
.execute(datasetPersistentId, datasetVersion.toString(), FileDownloadSizeMode.ARCHIVAL)
.catch((error: WriteError) => {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should never throw a WriteError, but rather a ReadError, as it is a read operation.

I'm not sure if this happens elsewhere too, could you check?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I added 2 commits

  1. I found that the error when running the dev-env was because the package-lock.json wasn't being copied to the docker image so there were some conflicts between the dependencies because npm was using the last version of the libraries. I added a commit to fix this
  2. You're right about the error types, I fixed it everywhere and I'll put more attention next time when adding those types, thanks!

throw new Error(error.message)
})
}

getUserPermissionsById(id: number): Promise<FileUserPermissions> {
return getFileUserPermissions
.execute(id)
Expand Down
8 changes: 4 additions & 4 deletions src/files/infrastructure/mappers/JSFileMapper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -82,14 +82,14 @@ export class JSFileMapper {
): FileVersion {
const fileVersion = { number: jsVersion, publishingStatus: FilePublishingStatus.DRAFT }

if (datasetVersion.publishingStatus === DatasetPublishingStatus.DEACCESSIONED) {
fileVersion.publishingStatus = FilePublishingStatus.DEACCESSIONED
}

if (jsPublicationDate) {
fileVersion.publishingStatus = FilePublishingStatus.RELEASED
}

if (datasetVersion.publishingStatus === DatasetPublishingStatus.DEACCESSIONED) {
fileVersion.publishingStatus = FilePublishingStatus.DEACCESSIONED
}

return fileVersion
}

Expand Down
10 changes: 8 additions & 2 deletions src/sections/dataset/dataset-files/DatasetFiles.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ export function DatasetFiles({
}: DatasetFilesProps) {
const [paginationInfo, setPaginationInfo] = useState<FilePaginationInfo>(new FilePaginationInfo())
const [criteria, setCriteria] = useState<FileCriteria>(new FileCriteria())
const { files, isLoading, filesCountInfo } = useFiles(
const { files, isLoading, filesCountInfo, filesTotalDownloadSize } = useFiles(
filesRepository,
datasetPersistentId,
datasetVersion,
Expand All @@ -37,7 +37,13 @@ export function DatasetFiles({
onCriteriaChange={setCriteria}
filesCountInfo={filesCountInfo}
/>
<FilesTable files={files} isLoading={isLoading} paginationInfo={paginationInfo} />
<FilesTable
files={files}
isLoading={isLoading}
paginationInfo={paginationInfo}
filesTotalDownloadSize={filesTotalDownloadSize}
criteria={criteria}
/>
<FilesPagination
page={paginationInfo.page}
pageSize={paginationInfo.pageSize}
Expand Down
36 changes: 34 additions & 2 deletions src/sections/dataset/dataset-files/files-table/FilesTable.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,18 +7,46 @@ import { RowSelectionMessage } from './row-selection/RowSelectionMessage'
import { ZipDownloadLimitMessage } from './zip-download-limit-message/ZipDownloadLimitMessage'
import { SpinnerSymbol } from './spinner-symbol/SpinnerSymbol'
import { FilePaginationInfo } from '../../../../files/domain/models/FilePaginationInfo'
import { useEffect, useState } from 'react'
import { FileSelection } from './row-selection/useFileSelection'
import { FileCriteria } from '../../../../files/domain/models/FileCriteria'

interface FilesTableProps {
files: File[]
isLoading: boolean
paginationInfo: FilePaginationInfo
filesTotalDownloadSize: number
criteria: FileCriteria
}

export function FilesTable({ files, isLoading, paginationInfo }: FilesTableProps) {
export function FilesTable({
files,
isLoading,
paginationInfo,
filesTotalDownloadSize,
criteria
}: FilesTableProps) {
const { table, fileSelection, selectAllFiles, clearFileSelection } = useFilesTable(
files,
paginationInfo
)
const [visitedPagination, setVisitedPagination] = useState<FilePaginationInfo>(paginationInfo)
const [visitedFiles, setVisitedFiles] = useState<FileSelection>({})

useEffect(() => {
if (visitedPagination.page == paginationInfo.page) {
setVisitedFiles((visitedFiles) => ({ ...visitedFiles, ...fileSelection }))
}
setVisitedPagination(paginationInfo)
}, [fileSelection])

const [previousCriteria, setPreviousCriteria] = useState<FileCriteria>(criteria)
useEffect(() => {
if (previousCriteria != criteria) {
clearFileSelection()
}
setPreviousCriteria(criteria)
})

if (isLoading) {
return <SpinnerSymbol />
Expand All @@ -31,7 +59,11 @@ export function FilesTable({ files, isLoading, paginationInfo }: FilesTableProps
totalFilesCount={paginationInfo.totalFiles}
clearRowSelection={clearFileSelection}
/>
<ZipDownloadLimitMessage fileSelection={fileSelection} />
<ZipDownloadLimitMessage
fileSelection={fileSelection}
visitedFiles={visitedFiles}
filesTotalDownloadSize={filesTotalDownloadSize}
/>
<Table>
<FilesTableHeader headers={table.getHeaderGroups()} />
<FilesTableBody rows={table.getRowModel().rows} />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,10 @@ export function useFileSelection(
}
const selectAllFiles = () => {
setCurrentPageRowSelection(createRowSelection(paginationInfo.pageSize))
setFileSelection(createFileSelection(paginationInfo.totalFiles))

const totalFilesFileSelection = createFileSelection(paginationInfo.totalFiles)
const newFileSelection = { ...totalFilesFileSelection, ...fileSelection }
setFileSelection(newFileSelection)
}
const clearFileSelection = () => {
setCurrentPageRowSelection({})
Expand Down Expand Up @@ -92,7 +95,7 @@ export function createRowSelection(numberOfRows: number) {
return rowSelection
}

export function createFileSelection(numberOfRows: number) {
export function createFileSelection(numberOfRows: number): FileSelection {
const fileSelection: FileSelection = {}

for (let i = 0; i < numberOfRows; i++) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { useTranslation } from 'react-i18next'
import styles from './ZipLimitMessage.module.scss'
import { File, FileSizeUnit } from '../../../../../files/domain/models/File'
import { FileSizeUnit } from '../../../../../files/domain/models/File'
import { useSettings } from '../../../../settings/SettingsContext'
import { SettingName } from '../../../../../settings/domain/models/Setting'
import { ZipDownloadLimit } from '../../../../../settings/domain/models/ZipDownloadLimit'
Expand All @@ -9,11 +9,17 @@ import { FileSelection } from '../row-selection/useFileSelection'

interface ZipDownloadLimitMessageProps {
fileSelection: FileSelection
visitedFiles: FileSelection
filesTotalDownloadSize: number
}

const MINIMUM_FILES_TO_SHOW_MESSAGE = 1

export function ZipDownloadLimitMessage({ fileSelection }: ZipDownloadLimitMessageProps) {
export function ZipDownloadLimitMessage({
fileSelection,
visitedFiles,
filesTotalDownloadSize
}: ZipDownloadLimitMessageProps) {
const { t } = useTranslation('files')
const { getSettingByName } = useSettings()
const [zipDownloadLimitInBytes, setZipDownloadLimitInBytes] = useState<number>()
Expand All @@ -27,12 +33,20 @@ export function ZipDownloadLimitMessage({ fileSelection }: ZipDownloadLimitMessa
})
}, [getSettingByName])

// TODO - When selecting all files, the size should come from a call to a use case that returns the total size of the dataset files. Check issue https://github.com/IQSS/dataverse-frontend/issues/170
const selectionTotalSizeInBytes = getFilesTotalSizeInBytes(Object.values(fileSelection))
const [fileSelectionTotalSizeInInBytes, setFileSelectionTotalSizeInInBytes] = useState<number>(0)
useEffect(() => {
const totalSize = computeFileSelectionTotalSizeInBytes(
visitedFiles,
fileSelection,
filesTotalDownloadSize
)
setFileSelectionTotalSizeInInBytes(totalSize)
}, [fileSelection])

const showMessage =
zipDownloadLimitInBytes &&
Object.values(fileSelection).length > MINIMUM_FILES_TO_SHOW_MESSAGE &&
selectionTotalSizeInBytes > zipDownloadLimitInBytes
fileSelectionTotalSizeInInBytes > zipDownloadLimitInBytes

if (!showMessage) {
return <></>
Expand All @@ -41,18 +55,38 @@ export function ZipDownloadLimitMessage({ fileSelection }: ZipDownloadLimitMessa
<div className={styles.container}>
<span className={styles.message}>
{t('table.zipDownloadExceedsLimit', {
selectionTotalSize: bytesToHumanReadable(selectionTotalSizeInBytes),
selectionTotalSize: bytesToHumanReadable(fileSelectionTotalSizeInInBytes),
zipDownloadSizeLimit: bytesToHumanReadable(zipDownloadLimitInBytes)
})}
</span>
</div>
)
}

function getFilesTotalSizeInBytes(files: (File | undefined)[]) {
return files
.map((file) => file?.size)
.reduce((bytes, size) => bytes + (size ? size.toBytes() : 0), 0)
function computeFileSelectionTotalSizeInBytes(
visitedFiles: FileSelection,
fileSelection: FileSelection,
filesTotalDownloadSize: number
) {
const selectAllHasBeenClicked = Object.values(fileSelection).some((file) => file == undefined)
if (selectAllHasBeenClicked) {
const differenceBetweenPreviousAndCurrentSelection =
getFilesTotalSize(visitedFiles) - getFilesTotalSize(fileSelection)

if (differenceBetweenPreviousAndCurrentSelection < 0) {
return filesTotalDownloadSize
}

return filesTotalDownloadSize - differenceBetweenPreviousAndCurrentSelection
}

return getFilesTotalSize(fileSelection)
}

function getFilesTotalSize(fileSelection: FileSelection) {
return Object.values(fileSelection)
.filter((file) => file != undefined)
.reduce((totalSize, file) => totalSize + (file ? file.size.toBytes() : 0), 0)
}

function bytesToHumanReadable(bytes: number) {
Expand Down
15 changes: 14 additions & 1 deletion src/sections/dataset/dataset-files/useFiles.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import { FilePaginationInfo } from '../../../files/domain/models/FilePaginationI
import { useFilePermissions } from '../../file/file-permissions/FilePermissionsContext'
import { FilePermission } from '../../../files/domain/models/FileUserPermissions'
import { DatasetVersion } from '../../../dataset/domain/models/Dataset'
import { getFilesTotalDownloadSize } from '../../../files/domain/useCases/getFilesTotalDownloadSize'

export function useFiles(
filesRepository: FileRepository,
Expand All @@ -22,6 +23,7 @@ export function useFiles(
const [files, setFiles] = useState<File[]>([])
const [isLoading, setIsLoading] = useState<boolean>(true)
const [filesCountInfo, setFilesCountInfo] = useState<FilesCountInfo>()
const [filesTotalDownloadSize, setFilesTotalDownloadSize] = useState<number>(0)

useEffect(() => {
getFilesCountInfoByDatasetPersistentId(filesRepository, datasetPersistentId, datasetVersion)
Expand Down Expand Up @@ -65,9 +67,20 @@ export function useFiles(
}
}, [filesRepository, datasetPersistentId, datasetVersion, paginationInfo, criteria])

useEffect(() => {
getFilesTotalDownloadSize(filesRepository, datasetPersistentId, datasetVersion)
.then((filesTotalDownloadSize: number) => {
setFilesTotalDownloadSize(filesTotalDownloadSize)
})
.catch((error) => {
console.error('There was an error getting the files total download size', error)
})
}, [filesRepository, datasetPersistentId, datasetVersion])

return {
files,
isLoading,
filesCountInfo
filesCountInfo,
filesTotalDownloadSize
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ export class SettingJSDataverseRepository implements SettingRepository {
setTimeout(() => {
resolve({
name: SettingName.ZIP_DOWNLOAD_LIMIT,
value: new ZipDownloadLimit(500, FileSizeUnit.BYTES)
value: new ZipDownloadLimit(1, FileSizeUnit.BYTES)
} as Setting<T>)
}, 1000)
})
Expand Down
2 changes: 1 addition & 1 deletion src/stories/WithSettings.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { SettingMother } from '../../tests/component/settings/domain/models/Sett
import { ZipDownloadLimit } from '../settings/domain/models/ZipDownloadLimit'
import { FileSizeUnit } from '../files/domain/models/File'

const zipDownloadLimitMock = new ZipDownloadLimit(500, FileSizeUnit.BYTES)
const zipDownloadLimitMock = new ZipDownloadLimit(1, FileSizeUnit.BYTES)
export const WithSettings = (Story: StoryFn) => {
// eslint-disable-next-line unused-imports/no-unused-vars
function getSettingByName<T>(name: SettingName): Promise<Setting<T>> {
Expand Down
Loading
Loading