Skip to content

Commit cdc198c

Browse files
committed
feat: updateFileMetadata use case
1 parent e689f25 commit cdc198c

File tree

9 files changed

+160
-2
lines changed

9 files changed

+160
-2
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: 5 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(
@@ -63,4 +64,8 @@ export interface IFilesRepository {
6364
deleteFile(fileId: number | string): Promise<undefined>
6465

6566
restrictFile(fileId: number | string, restrict: boolean): Promise<undefined>
67+
updateFileMetadata(
68+
fileId: number | string,
69+
updateFileMetadataDTO: UpdateFileMetadataDTO
70+
): Promise<void>
6671
}
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
@@ -13,6 +13,7 @@ import { DirectUploadClient } from './infra/clients/DirectUploadClient'
1313
import { AddUploadedFilesToDataset } from './domain/useCases/AddUploadedFilesToDataset'
1414
import { DeleteFile } from './domain/useCases/DeleteFile'
1515
import { RestrictFile } from './domain/useCases/RestrictFile'
16+
import { UpdateFileMetadata } from './domain/useCases/UpdateFileMetadata'
1617

1718
const filesRepository = new FilesRepository()
1819
const directUploadClient = new DirectUploadClient(filesRepository)
@@ -30,6 +31,7 @@ const uploadFile = new UploadFile(directUploadClient)
3031
const addUploadedFilesToDataset = new AddUploadedFilesToDataset(filesRepository)
3132
const deleteFile = new DeleteFile(filesRepository)
3233
const restrictFile = new RestrictFile(filesRepository)
34+
const updateFileMetadata = new UpdateFileMetadata(filesRepository)
3335

3436
export {
3537
getDatasetFiles,
@@ -44,7 +46,8 @@ export {
4446
uploadFile,
4547
addUploadedFilesToDataset,
4648
deleteFile,
47-
restrictFile
49+
restrictFile,
50+
updateFileMetadata
4851
}
4952

5053
export { FileModel as File, FileEmbargo, FileChecksum } from './domain/models/FileModel'
@@ -74,3 +77,4 @@ export { FileDownloadSizeMode } from './domain/models/FileDownloadSizeMode'
7477
export { FilesSubset } from './domain/models/FilesSubset'
7578
export { FilePreview, FilePreviewChecksum } from './domain/models/FilePreview'
7679
export { UploadedFileDTO } from './domain/dtos/UploadedFileDTO'
80+
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 {
@@ -309,4 +310,23 @@ export class FilesRepository extends ApiRepository implements IFilesRepository {
309310
throw error
310311
})
311312
}
313+
314+
public async updateFileMetadata(
315+
fileId: string | number,
316+
updateFileMetadata: UpdateFileMetadataDTO
317+
): Promise<void> {
318+
const formData = new FormData()
319+
formData.append('jsonData', JSON.stringify(updateFileMetadata))
320+
321+
return this.doPost(
322+
this.buildApiEndpoint(this.filesResourceName, `${fileId}/metadata`),
323+
formData,
324+
{},
325+
ApiConstants.CONTENT_TYPE_MULTIPART_FORM_DATA
326+
)
327+
.then(() => undefined)
328+
.catch((error) => {
329+
throw error
330+
})
331+
}
312332
}

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: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -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()
@@ -854,4 +855,47 @@ describe('FilesRepository', () => {
854855
await expect(setFileToRestricted(nonExistentFiledId)).rejects.toThrow(expectedError)
855856
})
856857
})
858+
859+
describe('updateFileMetadata', () => {
860+
test('should update file metadata when file exists', async () => {
861+
const getDatasetFiles = await sut.getDatasetFiles(
862+
testDatasetIds.numericId,
863+
latestDatasetVersionId,
864+
false,
865+
FileOrderCriteria.NAME_AZ
866+
)
867+
const fileid = getDatasetFiles.files[0].id
868+
console.log('updateFileMetadata fileid', fileid)
869+
const testFileMetadata = {
870+
description: 'My description bbb.',
871+
categories: ['Data'],
872+
restrict: false
873+
}
874+
875+
const actual = await sut.updateFileMetadata(fileid, testFileMetadata)
876+
const getFileMetadataResult = await getFileMetadata(fileid).catch(() => {
877+
throw new Error(`Error while getting file metadata ${fileid}`)
878+
})
879+
880+
await new Promise((resolve) => setTimeout(resolve, 1000))
881+
expect(actual).toBeUndefined()
882+
expect(getFileMetadataResult.data.description).toBe(testFileMetadata.description)
883+
expect(getFileMetadataResult.data.categories).toEqual(testFileMetadata.categories)
884+
expect(getFileMetadataResult.data.restricted).toBe(testFileMetadata.restrict)
885+
})
886+
887+
test('should return error when file does not exist', async () => {
888+
const nonExistentFiledId = 4000
889+
const testFileMetadata = {
890+
description: 'My description bbb.',
891+
categories: ['Data'],
892+
restrict: false
893+
}
894+
const errorExpected = new WriteError(`[400] Error attempting get the requested data file.`)
895+
896+
await expect(sut.updateFileMetadata(nonExistentFiledId, testFileMetadata)).rejects.toThrow(
897+
errorExpected
898+
)
899+
})
900+
})
857901
})

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)