Skip to content

Commit 81255e0

Browse files
committed
feat: updateFileMetadata use case
1 parent c789d0f commit 81255e0

File tree

9 files changed

+162
-3
lines changed

9 files changed

+162
-3
lines changed
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
export interface UpdateFileMetadataDTO {
2+
description?: string
3+
prevFreeform?: string
4+
categories?: string[]
5+
dataFileTags?: string[]
6+
restrict?: boolean
7+
}

src/files/domain/repositories/IFilesRepository.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import { FileModel } from '../models/FileModel'
88
import { Dataset } from '../../../datasets'
99
import { FileUploadDestination } from '../models/FileUploadDestination'
1010
import { UploadedFileDTO } from '../dtos/UploadedFileDTO'
11+
import { UpdateFileMetadataDTO } from '../dtos/UpdateFileMetadataDTO'
1112

1213
export interface IFilesRepository {
1314
getDatasetFiles(
@@ -59,4 +60,9 @@ export interface IFilesRepository {
5960
datasetId: number | string,
6061
uploadedFileDTOs: UploadedFileDTO[]
6162
): Promise<undefined>
63+
64+
updateFileMetadata(
65+
fileId: number | string,
66+
updateFileMetadataDTO: UpdateFileMetadataDTO
67+
): Promise<void>
6268
}
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
import { UseCase } from '../../../core/domain/useCases/UseCase'
2+
import { IFilesRepository } from '../repositories/IFilesRepository'
3+
import { UpdateFileMetadataDTO } from '../dtos/UpdateFileMetadataDTO'
4+
5+
export class UpdateFileMetadata implements UseCase<void> {
6+
private filesRepository: IFilesRepository
7+
8+
constructor(filesRepository: IFilesRepository) {
9+
this.filesRepository = filesRepository
10+
}
11+
12+
/**
13+
* Updates the metadata for a particular File.
14+
*
15+
* @param {number | string} [fileId] - The file identifier, which can be a string (for persistent identifiers), or a number (for numeric identifiers).
16+
* @param {UpdateFileMetadataDTO} [updateFileMetadataDTO] - The DTO containing the metadata updates.
17+
* @returns {Promise<void>}
18+
*/
19+
async execute(
20+
fileId: number | string,
21+
updateFileMetadataDTO: UpdateFileMetadataDTO
22+
): Promise<void> {
23+
await this.filesRepository.updateFileMetadata(fileId, updateFileMetadataDTO)
24+
}
25+
}

src/files/index.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import { GetFileAndDataset } from './domain/useCases/GetFileAndDataset'
1111
import { UploadFile } from './domain/useCases/UploadFile'
1212
import { DirectUploadClient } from './infra/clients/DirectUploadClient'
1313
import { AddUploadedFilesToDataset } from './domain/useCases/AddUploadedFilesToDataset'
14+
import { UpdateFileMetadata } from './domain/useCases/UpdateFileMetadata'
1415

1516
const filesRepository = new FilesRepository()
1617
const directUploadClient = new DirectUploadClient(filesRepository)
@@ -26,6 +27,7 @@ const getFileAndDataset = new GetFileAndDataset(filesRepository)
2627
const getFileCitation = new GetFileCitation(filesRepository)
2728
const uploadFile = new UploadFile(directUploadClient)
2829
const addUploadedFilesToDataset = new AddUploadedFilesToDataset(filesRepository)
30+
const updateFileMetadata = new UpdateFileMetadata(filesRepository)
2931

3032
export {
3133
getDatasetFiles,
@@ -38,7 +40,8 @@ export {
3840
getFileAndDataset,
3941
getFileCitation,
4042
uploadFile,
41-
addUploadedFilesToDataset
43+
addUploadedFilesToDataset,
44+
updateFileMetadata
4245
}
4346

4447
export { FileModel as File, FileEmbargo, FileChecksum } from './domain/models/FileModel'
@@ -68,3 +71,4 @@ export { FileDownloadSizeMode } from './domain/models/FileDownloadSizeMode'
6871
export { FilesSubset } from './domain/models/FilesSubset'
6972
export { FilePreview, FilePreviewChecksum } from './domain/models/FilePreview'
7073
export { UploadedFileDTO } from './domain/dtos/UploadedFileDTO'
74+
export { UpdateFileMetadataDTO } from './domain/dtos/UpdateFileMetadataDTO'

src/files/infra/repositories/FilesRepository.ts

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ import { Dataset } from '../../../datasets'
1818
import { FileUploadDestination } from '../../domain/models/FileUploadDestination'
1919
import { transformUploadDestinationsResponseToUploadDestination } from './transformers/fileUploadDestinationsTransformers'
2020
import { UploadedFileDTO } from '../../domain/dtos/UploadedFileDTO'
21+
import { UpdateFileMetadataDTO } from '../../domain/dtos/UpdateFileMetadataDTO'
2122
import { ApiConstants } from '../../../core/infra/repositories/ApiConstants'
2223

2324
export interface GetFilesQueryParams {
@@ -293,4 +294,23 @@ export class FilesRepository extends ApiRepository implements IFilesRepository {
293294
queryParams.searchText = fileSearchCriteria.searchText
294295
}
295296
}
297+
298+
public async updateFileMetadata(
299+
fileId: string | number,
300+
updateFileMetadata: UpdateFileMetadataDTO
301+
): Promise<void> {
302+
const formData = new FormData()
303+
formData.append('jsonData', JSON.stringify(updateFileMetadata))
304+
305+
return this.doPost(
306+
this.buildApiEndpoint(this.filesResourceName, `${fileId}/metadata`),
307+
formData,
308+
{},
309+
ApiConstants.CONTENT_TYPE_MULTIPART_FORM_DATA
310+
)
311+
.then(() => undefined)
312+
.catch((error) => {
313+
throw error
314+
})
315+
}
296316
}

test/functional/users/DeleteCurrentApiToken.test.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,6 @@ describe('execute', () => {
2424
const testApiToken = await createApiTokenViaApi('deleteCurrentApiTokenFTUser')
2525
ApiConfig.init(TestConstants.TEST_API_URL, DataverseApiAuthMechanism.API_KEY, testApiToken)
2626
await deleteCurrentApiToken.execute()
27-
// Since the token has been deleted, the next call using it should return a WriteError
2827
await expect(deleteCurrentApiToken.execute()).rejects.toBeInstanceOf(WriteError)
2928
})
3029
})

test/integration/files/FilesRepository.test.ts

Lines changed: 45 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ import {
2828
} from '../../../src/datasets'
2929
import { FileModel } from '../../../src/files/domain/models/FileModel'
3030
import { FileCounts } from '../../../src/files/domain/models/FileCounts'
31-
import { FileDownloadSizeMode } from '../../../src'
31+
import { FileDownloadSizeMode, WriteError } from '../../../src'
3232
import {
3333
deaccessionDatasetViaApi,
3434
publishDatasetViaApi,
@@ -41,6 +41,7 @@ import {
4141
deleteCollectionViaApi,
4242
setStorageDriverViaApi
4343
} from '../../testHelpers/collections/collectionHelper'
44+
import { getFileMetadata } from '../../testHelpers/files/filesHelper'
4445

4546
describe('FilesRepository', () => {
4647
const sut: FilesRepository = new FilesRepository()
@@ -646,4 +647,47 @@ describe('FilesRepository', () => {
646647
).rejects.toThrow(errorExpected)
647648
})
648649
})
650+
651+
describe('updateFileMetadata', () => {
652+
test('should update file metadata when file exists', async () => {
653+
const getDatasetFiles = await sut.getDatasetFiles(
654+
testDatasetIds.numericId,
655+
latestDatasetVersionId,
656+
false,
657+
FileOrderCriteria.NAME_AZ
658+
)
659+
const fileid = getDatasetFiles.files[0].id
660+
console.log('updateFileMetadata fileid', fileid)
661+
const testFileMetadata = {
662+
description: 'My description bbb.',
663+
categories: ['Data'],
664+
restrict: false
665+
}
666+
667+
const actual = await sut.updateFileMetadata(fileid, testFileMetadata)
668+
const getFileMetadataResult = await getFileMetadata(fileid).catch(() => {
669+
throw new Error(`Error while getting file metadata ${fileid}`)
670+
})
671+
672+
await new Promise((resolve) => setTimeout(resolve, 1000))
673+
expect(actual).toBeUndefined()
674+
expect(getFileMetadataResult.data.description).toBe(testFileMetadata.description)
675+
expect(getFileMetadataResult.data.categories).toEqual(testFileMetadata.categories)
676+
expect(getFileMetadataResult.data.restricted).toBe(testFileMetadata.restrict)
677+
})
678+
679+
test('should return error when file does not exist', async () => {
680+
const nonExistentFiledId = 4000
681+
const testFileMetadata = {
682+
description: 'My description bbb.',
683+
categories: ['Data'],
684+
restrict: false
685+
}
686+
const errorExpected = new WriteError(`[400] Error attempting get the requested data file.`)
687+
688+
await expect(sut.updateFileMetadata(nonExistentFiledId, testFileMetadata)).rejects.toThrow(
689+
errorExpected
690+
)
691+
})
692+
})
649693
})

test/testHelpers/files/filesHelper.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -227,3 +227,11 @@ export const updateFileTabularTags = async (
227227
}
228228
)
229229
}
230+
231+
export const getFileMetadata = async (fileId: number): Promise<AxiosResponse> => {
232+
return await axios.get(`${TestConstants.TEST_API_URL}/files/${fileId}/metadata`, {
233+
headers: {
234+
'X-Dataverse-Key': process.env.TEST_API_KEY
235+
}
236+
})
237+
}
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
import { UpdateFileMetadata } from '../../../src/files/domain/useCases/UpdateFileMetadata'
2+
import { IFilesRepository } from '../../../src/files/domain/repositories/IFilesRepository'
3+
import { WriteError } from '../../../src/core/domain/repositories/WriteError'
4+
import { createFileMetadataWithCategories } from '../../testHelpers/files/filesHelper'
5+
6+
describe('UpdateFileMetadata', () => {
7+
const testFileMetadata = createFileMetadataWithCategories()
8+
test('should updated file metadata with correct parameters and id', async () => {
9+
const filesRepositoryStub: IFilesRepository = {} as IFilesRepository
10+
filesRepositoryStub.updateFileMetadata = jest.fn().mockResolvedValue(testFileMetadata)
11+
12+
const sut = new UpdateFileMetadata(filesRepositoryStub)
13+
14+
await sut.execute(1, testFileMetadata)
15+
16+
expect(filesRepositoryStub.updateFileMetadata).toHaveBeenCalledWith(1, testFileMetadata)
17+
expect(filesRepositoryStub.updateFileMetadata).toHaveBeenCalledTimes(1)
18+
})
19+
20+
test('should return the updated file metadata with correct parameters and persisten Id', async () => {
21+
const filesRepositoryStub: IFilesRepository = {
22+
updateFileMetadata: jest.fn().mockResolvedValue(testFileMetadata)
23+
} as unknown as IFilesRepository
24+
25+
const sut = new UpdateFileMetadata(filesRepositoryStub)
26+
27+
await sut.execute('doi:10.5072/FK2/HC6KTB', testFileMetadata)
28+
29+
expect(filesRepositoryStub.updateFileMetadata).toHaveBeenCalledWith(
30+
'doi:10.5072/FK2/HC6KTB',
31+
testFileMetadata
32+
)
33+
expect(filesRepositoryStub.updateFileMetadata).toHaveBeenCalledTimes(1)
34+
})
35+
36+
test('should throw an error if the repository throws an error', async () => {
37+
const filesRepositoryStub: IFilesRepository = {
38+
updateFileMetadata: jest.fn().mockRejectedValue(new WriteError())
39+
} as unknown as IFilesRepository
40+
41+
const sut = new UpdateFileMetadata(filesRepositoryStub)
42+
43+
await expect(sut.execute(1, testFileMetadata)).rejects.toThrow(WriteError)
44+
expect(filesRepositoryStub.updateFileMetadata).toHaveBeenCalledWith(1, testFileMetadata)
45+
})
46+
})

0 commit comments

Comments
 (0)