Skip to content

Commit 4239bb9

Browse files
committed
feat: get collection storage driver
1 parent 9c7e9d8 commit 4239bb9

File tree

7 files changed

+134
-1
lines changed

7 files changed

+134
-1
lines changed

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ This changelog follows the principles of [Keep a Changelog](https://keepachangel
1111
- Datasets: Added `updateDatasetLicense` use case and repository method to support Dataverse endpoint `PUT /datasets/{id}/license`, for updating dataset license or custom terms.
1212
- Datasets: Added `getDatasetStorageDriver` use case and repository method to support Dataverse endpoint `GET /datasets/{identifier}/storageDriver`, for retrieving dataset storage driver configuration with properties: name, type, label, directUpload, directDownload, and uploadOutOfBand.
1313
- Datasets: Added `updateDatasetLicense` use case and repository method to support Dataverse endpoint `PUT /datasets/{id}/license`, for updating dataset license or custom terms
14+
- Collections: Added `getCollectionStorageDriver` use case and repository method to support Dataverse endpoints `GET /admin/dataverse/{alias}/storageDriver` and `GET /admin/dataverse/{alias}/storageDriver?getEffective=true`, returning storage driver configuration and optionally resolving the effective driver up the parent chain.
1415
- New Use Case: [Get Collections For Linking Use Case](./docs/useCases.md#get-collections-for-linking).
1516
- New Use Case: [Create a Template](./docs/useCases.md#create-a-template) under Templates.
1617
- New Use Case: [Get a Template](./docs/useCases.md#get-a-template) under Templates.
@@ -23,6 +24,7 @@ This changelog follows the principles of [Keep a Changelog](https://keepachangel
2324
- Templates: Rename `CreateDatasetTemplateDTO` to `CreateTemplateDTO`.
2425
- Templates: Rename `createDatasetTemplate` repository method to `createTemplate`.
2526
- Templates: Rename `getDatasetTemplates` repository method to `getTemplatesByCollectionId`.
27+
- Storage Driver endpoints: map new response shape (`name`, `type`, `label`) replacing the legacy `message` field for dataset and collection storage driver retrieval.
2628

2729
### Fixed
2830

docs/useCases.md

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ The different use cases currently available in the package are classified below,
1313
- [Get a Collection](#get-a-collection)
1414
- [Get Collection Facets](#get-collection-facets)
1515
- [Get User Permissions on a Collection](#get-user-permissions-on-a-collection)
16+
- [Get Collection Storage Driver](#get-collection-storage-driver)
1617
- [List All Collection Items](#list-all-collection-items)
1718
- [List My Data Collection Items](#list-my-data-collection-items)
1819
- [Get Collection Featured Items](#get-collection-featured-items)
@@ -227,6 +228,30 @@ The `collectionIdOrAlias` is a generic collection identifier, which can be eithe
227228

228229
If no collection identifier is specified, the default collection identifier; `:root` will be used. If you want to search for a different collection, you must add the collection identifier as a parameter in the use case call.
229230

231+
#### Get Collection Storage Driver
232+
233+
Returns a [StorageDriver](../src/datasets/domain/models/StorageDriver.ts) instance describing the storage driver configuration for a collection, including name, type, label, and upload/download capabilities. You can optionally resolve the effective storage driver by traversing parent collections.
234+
235+
##### Example call:
236+
237+
```typescript
238+
import { getCollectionStorageDriver } from '@iqss/dataverse-client-javascript'
239+
240+
const collectionAlias = 'sampleCollection'
241+
242+
// Get the storage driver configured directly on the collection
243+
getCollectionStorageDriver.execute(collectionAlias).then((storageDriver) => {
244+
console.log(storageDriver.name, storageDriver.label)
245+
})
246+
247+
// Get the effective storage driver (looks up the parent chain)
248+
getCollectionStorageDriver.execute(collectionAlias, true).then((storageDriver) => {
249+
console.log(storageDriver.name, storageDriver.label)
250+
})
251+
```
252+
253+
_See [use case](../src/collections/domain/useCases/GetCollectionStorageDriver.ts)_ implementation.
254+
230255
#### List All Collection Items
231256

232257
Returns an instance of [CollectionItemSubset](../src/collections/domain/models/CollectionItemSubset.ts) that contains reduced information for each collection item that the calling user can access in the installation.
@@ -1421,6 +1446,7 @@ _See [use case](../src/datasets/domain/useCases/GetDatasetTemplates.ts)_ definit
14211446
#### Get Dataset Storage Driver
14221447

14231448
Returns a [StorageDriver](../src/datasets/domain/models/StorageDriver.ts) instance with storage driver configuration for a dataset, including properties like name, type, label, and upload/download capabilities.
1449+
If you're targeting Dataverse versions that previously returned the field `message` instead of `name`, this client transparently maps that legacy field to `name`.
14241450

14251451
##### Example call:
14261452

src/collections/domain/repositories/ICollectionsRepository.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import { CollectionItemType } from '../../../collections/domain/models/Collectio
1212
import { CollectionLinks } from '../models/CollectionLinks'
1313
import { CollectionSummary } from '../models/CollectionSummary'
1414
import { LinkingObjectType } from '../useCases/GetCollectionsForLinking'
15+
import { StorageDriver } from '../../../datasets/domain/models/StorageDriver'
1516

1617
export interface ICollectionsRepository {
1718
getCollection(collectionIdOrAlias: number | string): Promise<Collection>
@@ -68,4 +69,8 @@ export interface ICollectionsRepository {
6869
searchTerm: string,
6970
alreadyLinked: boolean
7071
): Promise<CollectionSummary[]>
72+
getCollectionStorageDriver(
73+
collectionIdOrAlias: number | string,
74+
getEffective?: boolean
75+
): Promise<StorageDriver>
7176
}
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 { StorageDriver } from '../../../datasets/domain/models/StorageDriver'
3+
import { ICollectionsRepository } from '../repositories/ICollectionsRepository'
4+
5+
export class GetCollectionStorageDriver implements UseCase<StorageDriver> {
6+
private readonly collectionsRepository: ICollectionsRepository
7+
8+
constructor(collectionsRepository: ICollectionsRepository) {
9+
this.collectionsRepository = collectionsRepository
10+
}
11+
12+
/**
13+
* Returns the storage driver information for a given Collection.
14+
*
15+
* @param {number | string} collectionIdOrAlias - The collection identifier, which can be a string (alias) or numeric id.
16+
* @param {boolean} [getEffective=false] - Whether to resolve the effective storage driver up the parent chain.
17+
* @returns {Promise<StorageDriver>}
18+
*/
19+
async execute(
20+
collectionIdOrAlias: number | string,
21+
getEffective = false
22+
): Promise<StorageDriver> {
23+
return this.collectionsRepository.getCollectionStorageDriver(collectionIdOrAlias, getEffective)
24+
}
25+
}

src/collections/index.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ import { LinkCollection } from './domain/useCases/LinkCollection'
1616
import { UnlinkCollection } from './domain/useCases/UnlinkCollection'
1717
import { GetCollectionLinks } from './domain/useCases/GetCollectionLinks'
1818
import { GetCollectionsForLinking } from './domain/useCases/GetCollectionsForLinking'
19+
import { GetCollectionStorageDriver } from './domain/useCases/GetCollectionStorageDriver'
1920

2021
const collectionsRepository = new CollectionsRepository()
2122

@@ -36,6 +37,7 @@ const linkCollection = new LinkCollection(collectionsRepository)
3637
const unlinkCollection = new UnlinkCollection(collectionsRepository)
3738
const getCollectionLinks = new GetCollectionLinks(collectionsRepository)
3839
const getCollectionsForLinking = new GetCollectionsForLinking(collectionsRepository)
40+
const getCollectionStorageDriver = new GetCollectionStorageDriver(collectionsRepository)
3941

4042
export {
4143
getCollection,
@@ -54,7 +56,8 @@ export {
5456
linkCollection,
5557
unlinkCollection,
5658
getCollectionLinks,
57-
getCollectionsForLinking
59+
getCollectionsForLinking,
60+
getCollectionStorageDriver
5861
}
5962
export { Collection, CollectionInputLevel } from './domain/models/Collection'
6063
export { CollectionFacet } from './domain/models/CollectionFacet'

src/collections/infra/repositories/CollectionsRepository.ts

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ import { ReadError } from '../../../core/domain/repositories/ReadError'
4040
import { CollectionLinks } from '../../domain/models/CollectionLinks'
4141
import { CollectionSummary } from '../../domain/models/CollectionSummary'
4242
import { LinkingObjectType } from '../../domain/useCases/GetCollectionsForLinking'
43+
import { StorageDriver } from '../../../datasets/domain/models/StorageDriver'
4344

4445
export interface NewCollectionRequestPayload {
4546
alias: string
@@ -528,4 +529,17 @@ export class CollectionsRepository extends ApiRepository implements ICollections
528529
throw error
529530
})
530531
}
532+
533+
public async getCollectionStorageDriver(
534+
collectionIdOrAlias: number | string,
535+
getEffective = false
536+
): Promise<StorageDriver> {
537+
const queryParams = getEffective ? { getEffective: true } : {}
538+
539+
return this.doGet(`/admin/dataverse/${collectionIdOrAlias}/storageDriver`, true, queryParams)
540+
.then((response) => response.data.data as StorageDriver)
541+
.catch((error) => {
542+
throw error
543+
})
544+
}
531545
}
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
import { GetCollectionStorageDriver } from '../../../src/collections/domain/useCases/GetCollectionStorageDriver'
2+
import { ICollectionsRepository } from '../../../src/collections/domain/repositories/ICollectionsRepository'
3+
import { StorageDriver } from '../../../src/datasets/domain/models/StorageDriver'
4+
import { ReadError } from '../../../src/core/domain/repositories/ReadError'
5+
6+
describe('GetCollectionStorageDriver (unit)', () => {
7+
const testStorageDriver: StorageDriver = {
8+
name: 'local',
9+
type: 'filesystem',
10+
label: 'Local Storage',
11+
directUpload: true,
12+
directDownload: true,
13+
uploadOutOfBand: false
14+
}
15+
16+
test('should return storage driver on repository success', async () => {
17+
const collectionsRepositoryStub: ICollectionsRepository = {} as ICollectionsRepository
18+
collectionsRepositoryStub.getCollectionStorageDriver = jest
19+
.fn()
20+
.mockResolvedValue(testStorageDriver)
21+
22+
const sut = new GetCollectionStorageDriver(collectionsRepositoryStub)
23+
24+
const actual = await sut.execute('collection-alias')
25+
26+
expect(actual).toEqual(testStorageDriver)
27+
expect(actual.name).toBe('local')
28+
expect(actual.type).toBe('filesystem')
29+
expect(actual.label).toBe('Local Storage')
30+
expect(actual.directUpload).toBe(true)
31+
expect(actual.directDownload).toBe(true)
32+
expect(actual.uploadOutOfBand).toBe(false)
33+
})
34+
35+
test('should pass getEffective flag to repository', async () => {
36+
const collectionsRepositoryStub: ICollectionsRepository = {} as ICollectionsRepository
37+
collectionsRepositoryStub.getCollectionStorageDriver = jest
38+
.fn()
39+
.mockResolvedValue(testStorageDriver)
40+
41+
const sut = new GetCollectionStorageDriver(collectionsRepositoryStub)
42+
43+
await sut.execute(123, true)
44+
45+
expect(collectionsRepositoryStub.getCollectionStorageDriver).toHaveBeenCalledWith(123, true)
46+
})
47+
48+
test('should throw on repository error', async () => {
49+
const collectionsRepositoryStub: ICollectionsRepository = {} as ICollectionsRepository
50+
collectionsRepositoryStub.getCollectionStorageDriver = jest
51+
.fn()
52+
.mockRejectedValue(new ReadError('[404] Collection not found'))
53+
54+
const sut = new GetCollectionStorageDriver(collectionsRepositoryStub)
55+
56+
await expect(sut.execute('missing')).rejects.toThrow(ReadError)
57+
})
58+
})

0 commit comments

Comments
 (0)