Skip to content

Commit

Permalink
Merge pull request IQSS#418 from IQSS/feature/417-integrate-file-uplo…
Browse files Browse the repository at this point in the history
…ad-page-with-use-cases

Feature/417 integrate file upload page with use cases (early implementation)
  • Loading branch information
GPortas authored Jul 24, 2024
2 parents 91b2db6 + 7d889e4 commit 4d27f8b
Show file tree
Hide file tree
Showing 22 changed files with 1,383 additions and 87 deletions.
62 changes: 60 additions & 2 deletions public/locales/en/uploadDatasetFiles.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,64 @@
{
"breadcrumbActionItem": "Upload files",
"cancel": "Cancel upload",
"info": "Drag and drop files here.",
"select": "Select files to add"
"info": "Drag and drop files and/or directories here.",
"select": "Select files to add",
"delete": "Delete",
"save": "Save",
"saveUploaded": "Save uploaded files",
"uploadedFileSize": "Uploaded file size",
"restricted": "Restricted",
"tags": {
"documentation": "Documentation",
"data": "Data",
"code": "Code",
"editTagOptions": "Edit tag options",
"customFileTag": "Custom file tag",
"creatingNewTag": "Creating a new tag will add it as a tag option for all files in this dataset.",
"addNewTag": "Add new file tag...",
"availableTagOptions": "Available tag options: ",
"apply": "Apply",
"close": "Close",
"addCustomTag": "Add new custom file tag..."
},
"restriction": {
"restrictAccess": "Restrict Access",
"restrictionInfoP1": "Restricting limits access to published files. People who want to use the restricted files can request access by default. If you disable request access, you must add information about access to the Terms of Access field.",
"restrictionInfoP2": "Learn about restricting files and dataset access in the User Guide.",
"termsOfAccess": "Terms of Access for Restricted Files",
"enableAccessRequest": "Enable access request",
"saveChanges": "Save Changes",
"cancelChanges": "Cancel Changes"
},
"fileForm": {
"fileName": "File name",
"filePath": "File path",
"description": "Description",
"tags": "Tags",
"selectTags": "Select tags",
"editTagOptions": "Edit tag options",
"plus": "Add new tag option"
},
"filesHeader": {
"save": "Save",
"cancel": "Cancel",
"editFiles": "Edit files",
"restrict": "Restrict",
"unrestrict": "Unrestrict",
"filesUploaded_one": "{{count}} file uploaded",
"filesUploaded_other": "{{count}} files uploaded",
"filesSelected_one": "{{count}} file selected",
"filesSelected_other": "{{count}} files selected",
"deleteSelected": "Delete selected",
"addTagsToSelected": "Add tags to selected",
"addTags": "Add tags",
"selectAll": "Select all"
},
"addTags": {
"title": "Add tags to selected files",
"customTag": "Custom tag",
"selectTags": "Select tags to add",
"saveChanges": "Save Changes",
"cancelChanges": "Cancel Changes"
}
}
File renamed without changes.
89 changes: 75 additions & 14 deletions src/files/domain/models/FileUploadState.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,26 @@ import { FileSize, FileSizeUnit } from './FileMetadata'

export interface FileUploadState {
progress: number
storageId?: string
progressHidden: boolean
fileSizeString: string
fileSize: number
fileLastModified: number
failed: boolean
done: boolean
removed: boolean
fileName: string
fileDir: string
fileType: string
key: string
description?: string
tags: string[]
restricted: boolean
}

export interface FileUploaderState {
state: Map<string, FileUploadState>
uploaded: FileUploadState[]
}

export class FileUploadTools {
Expand All @@ -20,19 +31,28 @@ export class FileUploadTools {
const key = this.key(file)
const newValue: FileUploadState = {
progress: 0,
storageId: undefined,
progressHidden: true,
fileSizeString: new FileSize(file.size, FileSizeUnit.BYTES).toString(),
fileSize: file.size,
fileLastModified: file.lastModified,
failed: false,
done: false,
removed: false
removed: false,
fileName: file.name,
fileDir: this.toDir(file.webkitRelativePath),
fileType: file.type,
key: key,
tags: [],
restricted: false
}
newState.set(key, newValue)
})
return { state: newState }
return { state: newState, uploaded: this.toUploaded(newState) }
}

static key(file: File): string {
return file.webkitRelativePath + file.name
return file.webkitRelativePath ? file.webkitRelativePath : file.name
}

static get(file: File, state: FileUploaderState): FileUploadState {
Expand All @@ -44,28 +64,50 @@ export class FileUploadTools {
progress: 0,
progressHidden: true,
fileSizeString: new FileSize(file.size, FileSizeUnit.BYTES).toString(),
fileSize: file.size,
fileLastModified: file.lastModified,
failed: false,
done: false,
removed: false
removed: false,
fileName: file.name,
fileDir: this.toDir(file.webkitRelativePath),
fileType: file.type,
key: this.key(file),
tags: [],
restricted: false
}
}

static progress(file: File, now: number, oldState: FileUploaderState): FileUploaderState {
const [newState, newValue] = this.toNewState(file, oldState)
newValue.progress = now
return newState
const fileUploadState = oldState.state.get(this.key(file))
if (fileUploadState) {
fileUploadState.progress = now
}
return { state: oldState.state, uploaded: this.toUploaded(oldState.state) }
}

static storageId(file: File, id: string, oldState: FileUploaderState): FileUploaderState {
const fileUploadState = oldState.state.get(this.key(file))
if (fileUploadState) {
fileUploadState.storageId = id
}
return { state: oldState.state, uploaded: this.toUploaded(oldState.state) }
}

static failed(file: File, oldState: FileUploaderState): FileUploaderState {
const [newState, newValue] = this.toNewState(file, oldState)
newValue.failed = true
return newState
const fileUploadState = oldState.state.get(this.key(file))
if (fileUploadState) {
fileUploadState.failed = true
}
return { state: oldState.state, uploaded: this.toUploaded(oldState.state) }
}

static done(file: File, oldState: FileUploaderState): FileUploaderState {
const [newState, newValue] = this.toNewState(file, oldState)
newValue.done = true
return newState
const fileUploadState = oldState.state.get(this.key(file))
if (fileUploadState) {
fileUploadState.done = true
}
return { state: oldState.state, uploaded: this.toUploaded(oldState.state) }
}

static removed(file: File, oldState: FileUploaderState): FileUploaderState {
Expand All @@ -80,12 +122,31 @@ export class FileUploadTools {
return newState
}

static delete(file: File, oldState: FileUploaderState): FileUploaderState {
oldState.state.delete(this.key(file))
return { state: oldState.state, uploaded: this.toUploaded(oldState.state) }
}

private static toNewState(
file: File,
oldState: FileUploaderState
): [FileUploaderState, FileUploadState] {
const newValue = this.get(file, oldState)
oldState.state.set(this.key(file), newValue)
return [{ state: oldState.state }, newValue]
return [{ state: oldState.state, uploaded: this.toUploaded(oldState.state) }, newValue]
}

private static toUploaded(state: Map<string, FileUploadState>): FileUploadState[] {
return Array.from(state.values())
.filter((x) => !x.removed && x.done)
.sort((a, b) => (a.fileDir + a.fileName).localeCompare(b.fileDir + b.fileName))
}

private static toDir(relativePath: string): string {
const parts = relativePath.split('/')
if (parts.length > 0) {
return parts.slice(0, parts.length - 1).join('/')
}
return relativePath
}
}
12 changes: 10 additions & 2 deletions src/files/domain/repositories/FileRepository.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,8 @@ import { DatasetVersion, DatasetVersionNumber } from '../../../dataset/domain/mo
import { FilePaginationInfo } from '../models/FilePaginationInfo'
import { FilePreview } from '../models/FilePreview'
import { FilesWithCount } from '../models/FilesWithCount'
import { FileHolder } from './File'
import { FileHolder } from '../models/FileHolder'
import { FileUploadState } from '../models/FileUploadState'

export interface FileRepository {
getAllByDatasetPersistentId: (
Expand Down Expand Up @@ -38,6 +39,13 @@ export interface FileRepository {
datasetId: number | string,
file: FileHolder,
progress: (now: number) => void,
abortController: AbortController
abortController: AbortController,
storageIdSetter: (storageId: string) => void
) => Promise<void>
addUploadedFiles: (datasetId: number | string, files: FileUploadState[]) => Promise<void>
addUploadedFile: (
datasetId: number | string,
file: FileHolder,
storageId: string
) => Promise<void>
}
31 changes: 31 additions & 0 deletions src/files/domain/useCases/addUploadedFiles.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import { FileUploadState } from '../models/FileUploadState'
import { FileRepository } from '../repositories/FileRepository'

export function addUploadedFiles(
fileRepository: FileRepository,
datasetId: number | string,
files: FileUploadState[],
done: () => void
): void {
fileRepository
.addUploadedFiles(datasetId, files)
.catch((error: Error) => {
throw new Error(error.message)
})
.finally(done)
}

export function addUploadedFile(
fileRepository: FileRepository,
datasetId: number | string,
file: File,
storageId: string,
done: () => void
): void {
fileRepository
.addUploadedFile(datasetId, { file: file }, storageId)
.catch((error: Error) => {
throw new Error(error.message)
})
.finally(done)
}
9 changes: 5 additions & 4 deletions src/files/domain/useCases/uploadFile.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,13 @@ export function uploadFile(
file: File,
done: () => void,
failed: () => void,
progress: (now: number) => void
progress: (now: number) => void,
storageIdSetter: (storageId: string) => void
): () => void {
const controller = new AbortController()
fileRepository
.uploadFile(datasetId, { file: file }, progress, controller)
.then(() => done())
.catch(() => failed())
.uploadFile(datasetId, { file: file }, progress, controller, storageIdSetter)
.then(done)
.catch(failed)
return () => controller.abort()
}
31 changes: 24 additions & 7 deletions src/files/infrastructure/FileJSDataverseRepository.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ import {
getFileDataTables,
getFileDownloadCount,
getFileUserPermissions,
uploadFile as jsUploadFile,
addUploadedFileToDataset,
ReadError
} from '@iqss/dataverse-client-javascript'
import { FileCriteria } from '../domain/models/FileCriteria'
Expand All @@ -29,7 +31,8 @@ import { JSFileMetadataMapper } from './mappers/JSFileMetadataMapper'
import { FilePermissions } from '../domain/models/FilePermissions'
import { JSFilePermissionsMapper } from './mappers/JSFilePermissionsMapper'
import { FilesWithCount } from '../domain/models/FilesWithCount'
import { FileHolder } from '../domain/repositories/File'
import { FileHolder } from '../domain/models/FileHolder'
import { FileUploadState } from '../domain/models/FileUploadState'

const includeDeaccessioned = true

Expand Down Expand Up @@ -284,12 +287,26 @@ export class FileJSDataverseRepository implements FileRepository {
}

uploadFile(
_datasetId: number | string,
_file: FileHolder,
_progress: (now: number) => void,
_abortController: AbortController
datasetId: number | string,
file: FileHolder,
progress: (now: number) => void,
abortController: AbortController,
storageIdSetter: (storageId: string) => void
): Promise<void> {
// TODO:
return new Promise(() => {})
return jsUploadFile
.execute(datasetId, file.file, progress, abortController)
.then(storageIdSetter)
.catch((error: ReadError) => {
throw new Error(error.message)
})
}

addUploadedFiles(_datasetId: number | string, _files: FileUploadState[]): Promise<void> {
// TODO: not yet implemented
return new Promise<void>(() => {})
}

addUploadedFile(datasetId: number | string, file: FileHolder, storageId: string): Promise<void> {
return addUploadedFileToDataset.execute(datasetId, file.file, storageId)
}
}
Loading

0 comments on commit 4d27f8b

Please sign in to comment.