Skip to content

Commit 7563c99

Browse files
committed
Refactor: passing UploadedFileDTO directly to AddUploadedFileToDataset instead of File Blob
1 parent ceba6f3 commit 7563c99

File tree

7 files changed

+84
-79
lines changed

7 files changed

+84
-79
lines changed

docs/useCases.md

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -917,14 +917,20 @@ This use case involves adding a file that has been previously uploaded to remote
917917

918918
```typescript
919919
import { addUploadedFileToDataset } from '@iqss/dataverse-client-javascript'
920+
import { UploadedFileDTO } from '@iqss/dataverse-client-javascript'
920921

921922
/* ... */
922923

923924
const datasetId: number | string = 123
924-
const file: File = new File(['content'], 'example.txt', { type: 'text/plain' })
925-
const storageId: string = 'some-storage-identifier'
925+
const uploadedFileDTO: UploadedFileDTO = {
926+
fileName: 'the-file-name',
927+
storageId: 'localstack1://mybucket:19121faf7e7-2c40322ff54e',
928+
checksumType: 'md5',
929+
checksumValue: 'ede3d3b685b4e137ba4cb2521329a75e',
930+
mimeType: 'text/plain'
931+
}
926932

927-
addUploadedFileToDataset.execute(datasetId, file, storageId).then(() => {
933+
addUploadedFileToDataset.execute(datasetId, uploadedFileDTO).then(() => {
928934
console.log('File added to the dataset successfully.')
929935
})
930936

@@ -935,7 +941,7 @@ _See [use case](../src/files/domain/useCases/AddUploadedFileToDataset.ts) implem
935941

936942
The `datasetId` parameter can be a string, for persistent identifiers, or a number, for numeric identifiers.
937943

938-
The `file` parameter is a subclass of Blob (Binary Large Object) that represents a file.
944+
The `uploadedFileDTO` parameter is an instance of [UploadedFileDTO](../src/files/domain/dtos/UploadedFileDTO.ts) and includes properties related to the uploaded file. These properties should be calculated from the File Blob object previously sent through the upload file use case.
939945

940946
The `storageId` parameter represents the storage identifier obtained after a successful call to the UploadFile use case.
941947

src/files/domain/clients/IDirectUploadClient.ts

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,4 @@ export interface IDirectUploadClient {
88
abortController: AbortController,
99
destination?: FileUploadDestination
1010
): Promise<string>
11-
12-
addUploadedFileToDataset(datasetId: number | string, file: File, storageId: string): Promise<void>
1311
}
Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,12 @@
11
import { UseCase } from '../../../core/domain/useCases/UseCase'
2-
import { IDirectUploadClient } from '../clients/IDirectUploadClient'
2+
import { UploadedFileDTO } from '../dtos/UploadedFileDTO'
3+
import { IFilesRepository } from '../repositories/IFilesRepository'
34

45
export class AddUploadedFileToDataset implements UseCase<void> {
5-
private directUploadClient: IDirectUploadClient
6+
private filesRepository: IFilesRepository
67

7-
constructor(directUploadClient: IDirectUploadClient) {
8-
this.directUploadClient = directUploadClient
8+
constructor(filesRepository: IFilesRepository) {
9+
this.filesRepository = filesRepository
910
}
1011

1112
/**
@@ -17,12 +18,11 @@ export class AddUploadedFileToDataset implements UseCase<void> {
1718
* Note: This use case can be used independently of the UploadFile use case, e.g., supporting scenarios in which the file already exists in S3 or has been uploaded via some out-of-band method.
1819
*
1920
* @param {number | string} [datasetId] - The dataset identifier, which can be a string (for persistent identifiers) or a number (for numeric identifiers).
20-
* @param {File} [file] - The file object that has been uploaded.
21-
* @param {string} [storageId] - The storage identifier associated with the uploaded file.
21+
* @param {UploadedFileDTO} [uploadedFileDTO] - A file DTO associated with the uploaded file.
2222
* @returns {Promise<void>} A promise that resolves when the file has been successfully added to the dataset.
2323
* @throws {DirectUploadClientError} - If there are errors while performing the operation.
2424
*/
25-
async execute(datasetId: number | string, file: File, storageId: string): Promise<void> {
26-
await this.directUploadClient.addUploadedFileToDataset(datasetId, file, storageId)
25+
async execute(datasetId: number | string, uploadedFileDTO: UploadedFileDTO): Promise<void> {
26+
await this.filesRepository.addUploadedFileToDataset(datasetId, uploadedFileDTO)
2727
}
2828
}

src/files/index.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ const getFile = new GetFile(filesRepository)
2525
const getFileAndDataset = new GetFileAndDataset(filesRepository)
2626
const getFileCitation = new GetFileCitation(filesRepository)
2727
const uploadFile = new UploadFile(directUploadClient)
28-
const addUploadedFileToDataset = new AddUploadedFileToDataset(directUploadClient)
28+
const addUploadedFileToDataset = new AddUploadedFileToDataset(filesRepository)
2929

3030
export {
3131
getDatasetFiles,
@@ -66,3 +66,4 @@ export {
6666
} from './domain/models/FileDataTable'
6767
export { FileDownloadSizeMode } from './domain/models/FileDownloadSizeMode'
6868
export { FilesSubset } from './domain/models/FilesSubset'
69+
export { UploadedFileDTO } from './domain/dtos/UploadedFileDTO'

src/files/infra/clients/DirectUploadClient.ts

Lines changed: 0 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,10 @@ import {
66
buildRequestConfig,
77
buildRequestUrl
88
} from '../../../core/infra/repositories/apiConfigBuilders'
9-
import * as crypto from 'crypto'
109
import { IFilesRepository } from '../../domain/repositories/IFilesRepository'
1110
import { FileUploadError } from './errors/FileUploadError'
1211
import { FilePartUploadError } from './errors/FilePartUploadError'
1312
import { MultipartCompletionError } from './errors/MultipartCompletionError'
14-
import { AddUploadedFileToDatasetError } from './errors/AddUploadedFileToDatasetError'
1513
import { UrlGenerationError } from './errors/UrlGenerationError'
1614
import { MultipartAbortError } from './errors/MultipartAbortError'
1715
import { FileUploadCancelError } from './errors/FileUploadCancelError'
@@ -21,8 +19,6 @@ export class DirectUploadClient implements IDirectUploadClient {
2119
private filesRepository: IFilesRepository
2220
private maxMultipartRetries: number
2321

24-
private readonly checksumAlgorithm: string = 'md5'
25-
2622
private readonly progressAfterUrlGeneration: number = 10
2723
private readonly progressAfterFileUpload: number = 100
2824

@@ -192,31 +188,4 @@ export class DirectUploadClient implements IDirectUploadClient {
192188
throw new MultipartCompletionError(fileName, datasetId, error.message)
193189
})
194190
}
195-
196-
public async addUploadedFileToDataset(
197-
datasetId: number | string,
198-
file: File,
199-
storageId: string
200-
): Promise<void> {
201-
const fileArrayBuffer = await file.arrayBuffer()
202-
const fileBuffer = Buffer.from(fileArrayBuffer)
203-
return await this.filesRepository
204-
.addUploadedFileToDataset(datasetId, {
205-
fileName: file.name,
206-
storageId: storageId,
207-
checksumType: this.checksumAlgorithm,
208-
checksumValue: this.calculateBlobChecksum(fileBuffer),
209-
mimeType: file.type
210-
})
211-
.then(() => undefined)
212-
.catch((error) => {
213-
throw new AddUploadedFileToDatasetError(file.name, datasetId, error.message)
214-
})
215-
}
216-
217-
private calculateBlobChecksum(blob: Buffer): string {
218-
const hash = crypto.createHash(this.checksumAlgorithm)
219-
hash.update(blob)
220-
return hash.digest('hex')
221-
}
222191
}

test/integration/files/DirectUploadClient.test.ts renamed to test/integration/files/DirectUpload.test.ts

Lines changed: 48 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -21,18 +21,21 @@ import {
2121
createSinglepartFileBlob
2222
} from '../../testHelpers/files/filesHelper'
2323
import { FileUploadCancelError } from '../../../src/files/infra/clients/errors/FileUploadCancelError'
24+
import * as crypto from 'crypto'
2425

25-
describe('DirectUploadClient', () => {
26+
describe('Direct Upload', () => {
2627
const testCollectionAlias = 'directUploadTestCollection'
2728
let testDataset1Ids: CreatedDatasetIdentifiers
2829
let testDataset2Ids: CreatedDatasetIdentifiers
2930

30-
const filesRepository = new FilesRepository()
31-
const sut: DirectUploadClient = new DirectUploadClient(filesRepository)
31+
const filesRepositorySut = new FilesRepository()
32+
const directUploadSut: DirectUploadClient = new DirectUploadClient(filesRepositorySut)
3233

3334
let singlepartFile: File
3435
let multipartFile: File
3536

37+
const checksumAlgorithm: string = 'md5'
38+
3639
beforeEach(() => {
3740
jest.clearAllMocks()
3841
})
@@ -79,9 +82,9 @@ describe('DirectUploadClient', () => {
7982

8083
expect(await singlepartFileExistsInBucket(singlepartFileUrl)).toBe(false)
8184

82-
// Test uploadFile method
85+
// Test DirectUpload.uploadFile method
8386

84-
const actualStorageId = await sut.uploadFile(
87+
const actualStorageId = await directUploadSut.uploadFile(
8588
testDataset1Ids.numericId,
8689
singlepartFile,
8790
progressMock,
@@ -96,9 +99,9 @@ describe('DirectUploadClient', () => {
9699
expect(progressMock).toHaveBeenCalledWith(100)
97100
expect(progressMock).toHaveBeenCalledTimes(2)
98101

99-
// Test addUploadedFileToDataset method
102+
// Test FilesRepository.addUploadedFileToDataset method
100103

101-
let datasetFiles = await filesRepository.getDatasetFiles(
104+
let datasetFiles = await filesRepositorySut.getDatasetFiles(
102105
testDataset1Ids.numericId,
103106
DatasetNotNumberedVersion.LATEST,
104107
true,
@@ -107,9 +110,20 @@ describe('DirectUploadClient', () => {
107110

108111
expect(datasetFiles.totalFilesCount).toBe(0)
109112

110-
await sut.addUploadedFileToDataset(testDataset1Ids.numericId, singlepartFile, actualStorageId)
113+
const fileArrayBuffer = await singlepartFile.arrayBuffer()
114+
const fileBuffer = Buffer.from(fileArrayBuffer)
115+
116+
const uploadedFileDTO = {
117+
fileName: singlepartFile.name,
118+
storageId: actualStorageId,
119+
checksumType: checksumAlgorithm,
120+
checksumValue: calculateBlobChecksum(fileBuffer),
121+
mimeType: singlepartFile.type
122+
}
111123

112-
datasetFiles = await filesRepository.getDatasetFiles(
124+
await filesRepositorySut.addUploadedFileToDataset(testDataset1Ids.numericId, uploadedFileDTO)
125+
126+
datasetFiles = await filesRepositorySut.getDatasetFiles(
113127
testDataset1Ids.numericId,
114128
DatasetNotNumberedVersion.LATEST,
115129
true,
@@ -122,7 +136,7 @@ describe('DirectUploadClient', () => {
122136
expect(datasetFiles.files[0].storageIdentifier).toContain('localstack1://mybucket:')
123137
})
124138

125-
test('should upload file and add it to the dataset when there are multiple destination URLs', async () => {
139+
test('should upload file when there are multiple destination URLs', async () => {
126140
const destination = await createTestFileUploadDestination(
127141
multipartFile,
128142
testDataset2Ids.numericId
@@ -131,9 +145,9 @@ describe('DirectUploadClient', () => {
131145
const progressMock = jest.fn()
132146
const abortController = new AbortController()
133147

134-
// Test uploadFile method
148+
// Test DirectUpload.uploadFile method
135149

136-
const actualStorageId = await sut.uploadFile(
150+
const actualStorageId = await directUploadSut.uploadFile(
137151
testDataset2Ids.numericId,
138152
multipartFile,
139153
progressMock,
@@ -148,9 +162,9 @@ describe('DirectUploadClient', () => {
148162
expect(progressMock).toHaveBeenCalledWith(100)
149163
expect(progressMock).toHaveBeenCalledTimes(4)
150164

151-
// Test addUploadedFileToDataset method
165+
// Test FilesRepository.addUploadedFileToDataset method
152166

153-
let datasetFiles = await filesRepository.getDatasetFiles(
167+
let datasetFiles = await filesRepositorySut.getDatasetFiles(
154168
testDataset2Ids.numericId,
155169
DatasetNotNumberedVersion.LATEST,
156170
true,
@@ -159,9 +173,20 @@ describe('DirectUploadClient', () => {
159173

160174
expect(datasetFiles.totalFilesCount).toBe(0)
161175

162-
await sut.addUploadedFileToDataset(testDataset2Ids.numericId, multipartFile, actualStorageId)
176+
const fileArrayBuffer = await multipartFile.arrayBuffer()
177+
const fileBuffer = Buffer.from(fileArrayBuffer)
163178

164-
datasetFiles = await filesRepository.getDatasetFiles(
179+
const uploadedFileDTO = {
180+
fileName: multipartFile.name,
181+
storageId: actualStorageId,
182+
checksumType: checksumAlgorithm,
183+
checksumValue: calculateBlobChecksum(fileBuffer),
184+
mimeType: multipartFile.type
185+
}
186+
187+
await filesRepositorySut.addUploadedFileToDataset(testDataset2Ids.numericId, uploadedFileDTO)
188+
189+
datasetFiles = await filesRepositorySut.getDatasetFiles(
165190
testDataset2Ids.numericId,
166191
DatasetNotNumberedVersion.LATEST,
167192
true,
@@ -188,7 +213,7 @@ describe('DirectUploadClient', () => {
188213
}, 50)
189214

190215
await expect(
191-
sut.uploadFile(
216+
directUploadSut.uploadFile(
192217
testDataset2Ids.numericId,
193218
multipartFile,
194219
progressMock,
@@ -221,4 +246,10 @@ describe('DirectUploadClient', () => {
221246
return false
222247
})
223248
}
249+
250+
const calculateBlobChecksum = (blob: Buffer): string => {
251+
const hash = crypto.createHash(checksumAlgorithm)
252+
hash.update(blob)
253+
return hash.digest('hex')
254+
}
224255
})
Lines changed: 16 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,35 +1,35 @@
1-
import { createSinglepartFileBlob } from '../../testHelpers/files/filesHelper'
2-
import { IDirectUploadClient } from '../../../src/files/domain/clients/IDirectUploadClient'
31
import { DirectUploadClientError } from '../../../src/files/domain/clients/DirectUploadClientError'
42
import { AddUploadedFileToDataset } from '../../../src/files/domain/useCases/AddUploadedFileToDataset'
3+
import { IFilesRepository } from '../../../src/files/domain/repositories/IFilesRepository'
54

65
describe('execute', () => {
7-
let testFile: File
8-
const testStorageId = 'test'
9-
10-
beforeAll(async () => {
11-
testFile = await createSinglepartFileBlob()
12-
})
6+
const testUploadedFileDTO = {
7+
fileName: 'testfile',
8+
storageId: 'testStorageId',
9+
checksumValue: 'testChecksumValue',
10+
checksumType: 'md5',
11+
mimeType: 'test/type'
12+
}
1313

1414
test('should return undefined on client success', async () => {
15-
const directUploadClientStub: IDirectUploadClient = {} as IDirectUploadClient
16-
directUploadClientStub.addUploadedFileToDataset = jest.fn().mockResolvedValue(undefined)
15+
const filesRepositoryStub: IFilesRepository = {} as IFilesRepository
16+
filesRepositoryStub.addUploadedFileToDataset = jest.fn().mockResolvedValue(undefined)
1717

18-
const sut = new AddUploadedFileToDataset(directUploadClientStub)
18+
const sut = new AddUploadedFileToDataset(filesRepositoryStub)
1919

20-
const actual = await sut.execute(1, testFile, testStorageId)
20+
const actual = await sut.execute(1, testUploadedFileDTO)
2121

2222
expect(actual).toEqual(undefined)
2323
})
2424

2525
test('should return error on client error', async () => {
26-
const directUploadClientStub: IDirectUploadClient = {} as IDirectUploadClient
27-
directUploadClientStub.addUploadedFileToDataset = jest
26+
const filesRepositoryStub: IFilesRepository = {} as IFilesRepository
27+
filesRepositoryStub.addUploadedFileToDataset = jest
2828
.fn()
2929
.mockRejectedValue(new DirectUploadClientError('test', 'test', 'test'))
3030

31-
const sut = new AddUploadedFileToDataset(directUploadClientStub)
31+
const sut = new AddUploadedFileToDataset(filesRepositoryStub)
3232

33-
await expect(sut.execute(1, testFile, testStorageId)).rejects.toThrow(DirectUploadClientError)
33+
await expect(sut.execute(1, testUploadedFileDTO)).rejects.toThrow(DirectUploadClientError)
3434
})
3535
})

0 commit comments

Comments
 (0)