Skip to content

Commit 803633e

Browse files
committed
feat: update use case for tags
1 parent 74a0660 commit 803633e

File tree

11 files changed

+585
-0
lines changed

11 files changed

+585
-0
lines changed

docs/useCases.md

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1345,6 +1345,53 @@ _See [use case](../src/files/domain/useCases/UpdateFileMetadata.ts) implementati
13451345

13461346
The `fileId` parameter can be a string, for persistent identifiers, or a number, for numeric identifiers.
13471347

1348+
#### Update File Categories
1349+
1350+
Updates Categories of a File.
1351+
1352+
###### Example call:
1353+
1354+
```typescript
1355+
import { updateFileCategories } from '@iqss/dataverse-client-javascript'
1356+
1357+
/* ... */
1358+
1359+
const fileId: number | string = 123
1360+
const categories = ['category 1', 'category 1']
1361+
const replace = true
1362+
1363+
await updateFileCategories.execute(fileId, categories, replace).then((fileId) => {
1364+
console.log(`File updated successfully with file ID: ${fileId}`)
1365+
})
1366+
```
1367+
1368+
_See [use case](../src/files/domain/useCases/updateFileCategories.ts) implementation_.
1369+
1370+
The `fileId` parameter can be a string, for persistent identifiers, or a number, for numeric identifiers.
1371+
1372+
#### Update File Tabular Tags
1373+
1374+
Updates Tabular Tags of a File.
1375+
1376+
###### Example call:
1377+
1378+
```typescript
1379+
import { updateFileTabularTags } from '@iqss/dataverse-client-javascript'
1380+
1381+
/* ... */
1382+
1383+
const fileId: number | string = 123
1384+
const tabularTags = ['Surveys']
1385+
1386+
await updateFileTabularTags.execute(fileId, tabularTags, replace).then((fileId) => {
1387+
console.log(`File updated successfully with file ID: ${fileId}`)
1388+
})
1389+
```
1390+
1391+
_See [use case](../src/files/domain/useCases/updateFileTabularTags.ts) implementation_.
1392+
1393+
The `fileId` parameter can be a string, for persistent identifiers, or a number, for numeric identifiers.
1394+
13481395
#### Delete a File
13491396

13501397
Deletes a File.

src/files/domain/repositories/IFilesRepository.ts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,4 +72,16 @@ export interface IFilesRepository {
7272
fileId: number | string,
7373
updateFileMetadataDTO: UpdateFileMetadataDTO
7474
): Promise<void>
75+
76+
updateFileTabularTags(
77+
fileId: number | string,
78+
tabularTags: string[],
79+
replace?: boolean
80+
): Promise<void>
81+
82+
updateFileCategories(
83+
fileId: number | string,
84+
categories: string[],
85+
replace?: boolean
86+
): Promise<void>
7587
}
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
import { UseCase } from '../../../core/domain/useCases/UseCase'
2+
import { IFilesRepository } from '../repositories/IFilesRepository'
3+
4+
export class UpdateFileCategories implements UseCase<void> {
5+
private filesRepository: IFilesRepository
6+
7+
constructor(filesRepository: IFilesRepository) {
8+
this.filesRepository = filesRepository
9+
}
10+
11+
/**
12+
* Updates the categories for a particular File.
13+
* More detailed information about updating a file's categories behavior can be found in https://guides.dataverse.org/en/latest/api/native-api.html#updating-file-metadata
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 {string[]} [categories] - The categories to be added to the file.
17+
* @param {boolean} [replace] - If true, replaces the existing categories with the new ones. If false, adds the new categories to the existing ones.
18+
* @returns {Promise<void>}
19+
*/
20+
async execute(fileId: number | string, categories: string[], replace?: boolean): Promise<void> {
21+
await this.filesRepository.updateFileCategories(fileId, categories, replace)
22+
}
23+
}
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
import { UseCase } from '../../../core/domain/useCases/UseCase'
2+
import { IFilesRepository } from '../repositories/IFilesRepository'
3+
4+
export class UpdateFileTabularTags implements UseCase<void> {
5+
private filesRepository: IFilesRepository
6+
7+
constructor(filesRepository: IFilesRepository) {
8+
this.filesRepository = filesRepository
9+
}
10+
11+
/**
12+
* Updates the tabular tabular Tags for a particular File.
13+
* More detailed information about updating a file's tabularTags behavior can be found in https://guides.dataverse.org/en/latest/api/native-api.html#updating-file-metadata
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 {string[]} [tabularTags] - The tabular tags to be added to the file.
17+
* @param {boolean} [replace] - If true, replaces the existing tabularTags with the new ones. If false, adds the new tabularTags to the existing ones.
18+
* @returns {Promise<void>}
19+
*/
20+
async execute(fileId: number | string, tabularTags: string[], replace?: boolean): Promise<void> {
21+
await this.filesRepository.updateFileTabularTags(fileId, tabularTags, replace)
22+
}
23+
}

src/files/index.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@ import { DeleteFile } from './domain/useCases/DeleteFile'
1515
import { ReplaceFile } from './domain/useCases/ReplaceFile'
1616
import { RestrictFile } from './domain/useCases/RestrictFile'
1717
import { UpdateFileMetadata } from './domain/useCases/UpdateFileMetadata'
18+
import { UpdateFileTabularTags } from './domain/useCases/UpdateFileTabularTags'
19+
import { UpdateFileCategories } from './domain/useCases/UpdateFileCategories'
1820

1921
const filesRepository = new FilesRepository()
2022
const directUploadClient = new DirectUploadClient(filesRepository)
@@ -34,6 +36,8 @@ const deleteFile = new DeleteFile(filesRepository)
3436
const replaceFile = new ReplaceFile(filesRepository)
3537
const restrictFile = new RestrictFile(filesRepository)
3638
const updateFileMetadata = new UpdateFileMetadata(filesRepository)
39+
const updateFileTabularTags = new UpdateFileTabularTags(filesRepository)
40+
const updateFileCategories = new UpdateFileCategories(filesRepository)
3741

3842
export {
3943
getDatasetFiles,
@@ -50,6 +54,8 @@ export {
5054
deleteFile,
5155
restrictFile,
5256
updateFileMetadata,
57+
updateFileTabularTags,
58+
updateFileCategories,
5359
replaceFile
5460
}
5561

src/files/infra/repositories/FilesRepository.ts

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -381,4 +381,36 @@ export class FilesRepository extends ApiRepository implements IFilesRepository {
381381
throw error
382382
})
383383
}
384+
385+
public async updateFileTabularTags(
386+
fileId: number | string,
387+
tabularTags: string[],
388+
replace?: boolean
389+
): Promise<void> {
390+
return this.doPost(
391+
this.buildApiEndpoint(this.filesResourceName, 'metadata/tabularTags', fileId),
392+
{ tabularTags },
393+
{ replace: replace ?? false }
394+
)
395+
.then(() => undefined)
396+
.catch((error) => {
397+
throw error
398+
})
399+
}
400+
401+
public async updateFileCategories(
402+
fileId: number | string,
403+
categories: string[],
404+
replace?: boolean
405+
): Promise<void> {
406+
return this.doPost(
407+
this.buildApiEndpoint(this.filesResourceName, 'metadata/categories', fileId),
408+
{ categories },
409+
{ replace: replace ?? false }
410+
)
411+
.then(() => undefined)
412+
.catch((error) => {
413+
throw error
414+
})
415+
}
384416
}
Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,115 @@
1+
import {
2+
ApiConfig,
3+
createDataset,
4+
CreatedDatasetIdentifiers,
5+
WriteError,
6+
updateFileCategories,
7+
getFile,
8+
DatasetNotNumberedVersion,
9+
getDatasetFiles
10+
} from '../../../src'
11+
import { DataverseApiAuthMechanism } from '../../../src/core/infra/repositories/ApiConfig'
12+
import {
13+
createCollectionViaApi,
14+
deleteCollectionViaApi
15+
} from '../../testHelpers/collections/collectionHelper'
16+
import { deleteUnpublishedDatasetViaApi } from '../../testHelpers/datasets/datasetHelper'
17+
import { uploadFileViaApi } from '../../testHelpers/files/filesHelper'
18+
import { TestConstants } from '../../testHelpers/TestConstants'
19+
import { FileModel } from '../../../src/files/domain/models/FileModel'
20+
21+
describe('execute', () => {
22+
const testCollectionAlias = 'updateFileMetadataFunctionalTest'
23+
let testDatasetIds: CreatedDatasetIdentifiers
24+
const testTextFile1Name = 'test-file-1.txt'
25+
const metadataUpdate = ['file']
26+
27+
beforeAll(async () => {
28+
ApiConfig.init(
29+
TestConstants.TEST_API_URL,
30+
DataverseApiAuthMechanism.API_KEY,
31+
process.env.TEST_API_KEY
32+
)
33+
await createCollectionViaApi(testCollectionAlias)
34+
35+
try {
36+
testDatasetIds = await createDataset.execute(
37+
TestConstants.TEST_NEW_DATASET_DTO,
38+
testCollectionAlias
39+
)
40+
} catch (error) {
41+
throw new Error('Tests beforeAll(): Error while creating test dataset')
42+
}
43+
44+
await uploadFileViaApi(testDatasetIds.numericId, testTextFile1Name).catch(() => {
45+
throw new Error(`Tests beforeAll(): Error while uploading file ${testTextFile1Name}`)
46+
})
47+
})
48+
49+
afterAll(async () => {
50+
try {
51+
await deleteUnpublishedDatasetViaApi(testDatasetIds.numericId)
52+
} catch (error) {
53+
throw new Error('Tests afterAll(): Error while deleting test dataset')
54+
}
55+
56+
try {
57+
await deleteCollectionViaApi(testCollectionAlias)
58+
} catch (error) {
59+
throw new Error('Tests afterAll(): Error while deleting test collection')
60+
}
61+
})
62+
63+
test('should successfully update categories of a file', async () => {
64+
const datasetFiles = await getDatasetFiles.execute(testDatasetIds.numericId)
65+
const fileId = datasetFiles.files[0].id
66+
67+
try {
68+
await updateFileCategories.execute(fileId, metadataUpdate)
69+
} catch (error) {
70+
throw new Error('File metadata should be updated')
71+
} finally {
72+
const fileInfo: FileModel = (await getFile.execute(
73+
fileId,
74+
DatasetNotNumberedVersion.LATEST
75+
)) as FileModel
76+
77+
expect(fileInfo.categories).toEqual(metadataUpdate)
78+
}
79+
})
80+
81+
test('should successfully update categories of a file with replace parameter', async () => {
82+
const datasetFiles = await getDatasetFiles.execute(testDatasetIds.numericId)
83+
const fileId = datasetFiles.files[0].id
84+
const newCategories = ['new Category']
85+
try {
86+
await updateFileCategories.execute(fileId, newCategories, true)
87+
} catch (error) {
88+
throw new Error('File metadata should be updated')
89+
} finally {
90+
const fileInfo: FileModel = (await getFile.execute(
91+
fileId,
92+
DatasetNotNumberedVersion.LATEST
93+
)) as FileModel
94+
95+
expect(fileInfo.categories).toEqual(newCategories)
96+
}
97+
})
98+
99+
test('should throw an error when the file id does not exist', async () => {
100+
let writeError: WriteError | undefined = undefined
101+
const nonExistentFileId = 5
102+
103+
try {
104+
await updateFileCategories.execute(nonExistentFileId, metadataUpdate)
105+
throw new Error('Use case should throw an error')
106+
} catch (error) {
107+
writeError = error as WriteError
108+
} finally {
109+
expect(writeError).toBeInstanceOf(WriteError)
110+
expect(writeError?.message).toEqual(
111+
`There was an error when writing the resource. Reason was: [404] File with ID ${nonExistentFileId} not found.`
112+
)
113+
}
114+
})
115+
})

0 commit comments

Comments
 (0)