Skip to content
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ This changelog follows the principles of [Keep a Changelog](https://keepachangel
- Datasets: Added `exportDatasetMetadata` use case, repository method, and `ExportedDatasetMetadata` response type to support exporting dataset metadata by numeric id or persistent id through Dataverse endpoint `GET /datasets/export`.
- Collections: Added `allowedDatasetTypes` field to the [Collection](./src/collections/domain/models/Collection.ts) model. This field is optional and only populated the feature is enabled on the installation and configured on the collection.
- Collections: Added theme information when retrieving a collection using `getCollection`.
- Collections: Added `setDefaultContributorRole` use case.

### Changed

Expand Down
22 changes: 22 additions & 0 deletions docs/useCases.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ The different use cases currently available in the package are classified below,
- [Update Collection Featured Items](#update-collection-featured-items)
- [Delete Collection Featured Items](#delete-collection-featured-items)
- [Delete a Collection Featured Item](#delete-a-collection-featured-item)
- [Set Default Contributor Role](#set-default-contributor-role)
- [Templates](#Templates)
- [Templates read use cases](#templates-read-use-cases)
- [Get a Template](#get-a-template)
Expand Down Expand Up @@ -732,6 +733,27 @@ deleteCollectionFeaturedItem.execute(featuredItemId)

_See [use case](../src/collections/domain/useCases/DeleteCollectionFeaturedItem.ts)_ definition.

#### Set Default Contributor Role

Sets the default contributor role of a collection, given a collection identifier and a role alias.

##### Example call:

```typescript
import { setDefaultContributorRole } from '@iqss/dataverse-client-javascript'

/* ... */

const collectionIdOrAlias = 12345
const roleAlias = 'curator'

setDefaultContributorRole.execute(collectionIdOrAlias, roleAlias)

Comment thread
vera marked this conversation as resolved.
/* ... */
```

_See [use case](../src/collections/domain/useCases/SetDefaultContributorRole.ts)_ definition.

## Templates

### Templates Read Use Cases
Expand Down
5 changes: 5 additions & 0 deletions src/collections/domain/repositories/ICollectionsRepository.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import { CollectionSummary } from '../models/CollectionSummary'
import { AllowedStorageDrivers } from '../models/AllowedStorageDrivers'
import { StorageDriver } from '../../../core/domain/models/StorageDriver'
import { LinkingObjectType } from '../useCases/GetCollectionsForLinking'
import { RoleAlias } from '../../../roles/domain/models/RoleAlias'

export interface ICollectionsRepository {
getCollection(collectionIdOrAlias: number | string): Promise<Collection>
Expand All @@ -39,6 +40,10 @@ export interface ICollectionsRepository {
getCollectionUserPermissions(
collectionIdOrAlias: number | string
): Promise<CollectionUserPermissions>
setDefaultContributorRole(
collectionIdOrAlias: number | string,
roleAlias: RoleAlias | string
): Promise<void>
getCollectionItems(
collectionId?: string,
limit?: number,
Expand Down
27 changes: 27 additions & 0 deletions src/collections/domain/useCases/SetDefaultContributorRole.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import { UseCase } from '../../../core/domain/useCases/UseCase'
import { ICollectionsRepository } from '../repositories/ICollectionsRepository'
import { ROOT_COLLECTION_ID } from '../models/Collection'
import { RoleAlias } from '../../../roles/domain/models/RoleAlias'

export class SetDefaultContributorRole implements UseCase<void> {
private collectionsRepository: ICollectionsRepository

constructor(collectionsRepository: ICollectionsRepository) {
this.collectionsRepository = collectionsRepository
}

/**
* Sets the default Role that is assigned to contributors in the given collection.
*
* @param {number | string} [collectionIdOrAlias = ':root'] - A generic collection identifier, which can be either a string (for queries by CollectionAlias), or a number (for queries by CollectionId)
* If this parameter is not set, the default value is: ':root'
* @param {RoleAlias | string} [roleAlias] - The alias of the role to be assigned
* @returns {Promise<void>}
*/
async execute(
collectionIdOrAlias: number | string = ROOT_COLLECTION_ID,
roleAlias: RoleAlias | string
): Promise<void> {
return await this.collectionsRepository.setDefaultContributorRole(collectionIdOrAlias, roleAlias)
}
}
5 changes: 4 additions & 1 deletion src/collections/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import { GetCollectionsForLinking } from './domain/useCases/GetCollectionsForLin
import { SetCollectionStorageDriver } from './domain/useCases/SetCollectionStorageDriver'
import { DeleteCollectionStorageDriver } from './domain/useCases/DeleteCollectionStorageDriver'
import { GetAllowedCollectionStorageDrivers } from './domain/useCases/GetAllowedCollectionStorageDrivers'
import { SetDefaultContributorRole } from './domain/useCases/SetDefaultContributorRole'

const collectionsRepository = new CollectionsRepository()

Expand All @@ -46,6 +47,7 @@ const deleteCollectionStorageDriver = new DeleteCollectionStorageDriver(collecti
const getAllowedCollectionStorageDrivers = new GetAllowedCollectionStorageDrivers(
collectionsRepository
)
const setDefaultContributorRole = new SetDefaultContributorRole(collectionsRepository)

export {
getCollection,
Expand All @@ -68,7 +70,8 @@ export {
getCollectionsForLinking,
setCollectionStorageDriver,
deleteCollectionStorageDriver,
getAllowedCollectionStorageDrivers
getAllowedCollectionStorageDrivers,
setDefaultContributorRole
}
export { Collection, CollectionInputLevel, CollectionTheme } from './domain/models/Collection'
export { CollectionFacet } from './domain/models/CollectionFacet'
Expand Down
15 changes: 15 additions & 0 deletions src/collections/infra/repositories/CollectionsRepository.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ import { CollectionSummary } from '../../domain/models/CollectionSummary'
import { AllowedStorageDrivers } from '../../domain/models/AllowedStorageDrivers'
import { StorageDriver } from '../../../core/domain/models/StorageDriver'
import { LinkingObjectType } from '../../domain/useCases/GetCollectionsForLinking'
import { RoleAlias } from '../../../roles/domain/models/RoleAlias'

export interface NewCollectionRequestPayload {
alias: string
Expand Down Expand Up @@ -225,6 +226,20 @@ export class CollectionsRepository extends ApiRepository implements ICollections
})
}

public async setDefaultContributorRole(
collectionIdOrAlias: number | string,
roleAlias: RoleAlias | string
): Promise<void> {
return this.doPut(
`/${this.collectionsResourceName}/${collectionIdOrAlias}/defaultContributorRole/${roleAlias}`,
{}
)
.then(() => undefined)
.catch((error) => {
throw error
})
}
Comment thread
vera marked this conversation as resolved.

public async getCollectionItems(
collectionId?: string,
limit?: number,
Expand Down
12 changes: 12 additions & 0 deletions src/roles/domain/models/RoleAlias.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
// Aliases of built-in roles
export enum RoleAlias {
ADMIN = 'admin',
FILE_DOWNLOADER = 'fileDownloader',
FULL_CONTRIBUTOR = 'fullContributor',
DV_CONTRIBUTOR = 'dvContributor',
DS_CONTRIBUTOR = 'dsContributor',
EDITOR = 'contributor',
MANAGER = 'manager',
CURATOR = 'curator',
MEMBER = 'member'
}
1 change: 1 addition & 0 deletions src/roles/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,4 @@ const getUserSelectableRoles = new GetUserSelectableRoles(rolesRepository)
export { getUserSelectableRoles }

export { Role } from './domain/models/Role'
export { RoleAlias } from './domain/models/RoleAlias'
55 changes: 55 additions & 0 deletions test/integration/collections/SetDefaultContributorRole.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
import { SetDefaultContributorRole } from '../../../src/collections/domain/useCases/SetDefaultContributorRole'
import { CollectionsRepository } from '../../../src/collections/infra/repositories/CollectionsRepository'
import { ApiConfig } from '../../../src'
import { TestConstants } from '../../testHelpers/TestConstants'
import { DataverseApiAuthMechanism } from '../../../src/core/infra/repositories/ApiConfig'
import {
createCollectionViaApi,
deleteCollectionViaApi
} from '../../testHelpers/collections/collectionHelper'
import { RoleAlias } from '../../../src/roles/domain/models/RoleAlias'
import { WriteError } from '../../../src/core/domain/repositories/WriteError'

describe('SetDefaultContributorRole', () => {
const collectionsRepository = new CollectionsRepository()
const useCase = new SetDefaultContributorRole(collectionsRepository)
const testCollectionAlias = 'setDefaultContributorRoleTestCollection'

beforeAll(async () => {
ApiConfig.init(
TestConstants.TEST_API_URL,
DataverseApiAuthMechanism.API_KEY,
process.env.TEST_API_KEY
)
await createCollectionViaApi(testCollectionAlias)
})

afterAll(async () => {
await deleteCollectionViaApi(testCollectionAlias)
})

test('should successfully set the default contributor role', async () => {
const roleAlias = RoleAlias.CURATOR

await expect(useCase.execute(testCollectionAlias, roleAlias)).resolves.toBeUndefined()
})

test('should successfully set the default contributor role for the root collection', async () => {
const roleAlias = RoleAlias.CURATOR

await expect(useCase.execute(undefined, roleAlias)).resolves.toBeUndefined()
})

test('should throw an error when the collection does not exist', async () => {
const nonExistentCollection = 'nonExistentCollection'
const roleAlias = RoleAlias.CURATOR

await expect(useCase.execute(nonExistentCollection, roleAlias)).rejects.toThrow(WriteError)
})

test('should throw an error when the role alias does not exist', async () => {
const nonExistentRoleAlias = 'invalidRoleAlias'

await expect(useCase.execute(testCollectionAlias, nonExistentRoleAlias)).rejects.toThrow(WriteError)
})
})
37 changes: 37 additions & 0 deletions test/unit/collections/CollectionsRepository.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ import {
OrderType,
SortType
} from '../../../src/collections/domain/models/CollectionSearchCriteria'
import { RoleAlias } from '../../../src/roles/domain/models/RoleAlias'

describe('CollectionsRepository', () => {
const sut: CollectionsRepository = new CollectionsRepository()
Expand Down Expand Up @@ -97,6 +98,14 @@ describe('CollectionsRepository', () => {
data: createCollectionPayload()
}
}
const testSetDefaultContributorRoleResponse = {
data: {
status: 'OK',
data: {
message: 'Default contributor role has been set'
}
}
}
const testCollectionModel = createCollectionModel()

beforeEach(() => {
Expand Down Expand Up @@ -893,4 +902,32 @@ describe('CollectionsRepository', () => {
})
})
})

describe('setDefaultContributorRole', () => {
const testRoleAlias = RoleAlias.CURATOR

test('should call the API', async () => {
jest.spyOn(axios, 'put').mockResolvedValue(testSetDefaultContributorRoleResponse)
const expectedApiEndpoint = `${TestConstants.TEST_API_URL}/dataverses/test-collection/defaultContributorRole/${testRoleAlias}`

await sut.setDefaultContributorRole('test-collection', testRoleAlias)

expect(axios.put).toHaveBeenCalledWith(
expectedApiEndpoint,
'{}',
TestConstants.TEST_EXPECTED_AUTHENTICATED_REQUEST_CONFIG_API_KEY
)
})

test('should return error result on error response', async () => {
jest.spyOn(axios, 'put').mockRejectedValue(TestConstants.TEST_ERROR_RESPONSE)
let error = undefined as unknown as WriteError

await sut
.setDefaultContributorRole('test-collection', testRoleAlias)
.catch((e) => (error = e))

expect(error).toBeInstanceOf(Error)
})
})
})
26 changes: 26 additions & 0 deletions test/unit/collections/SetDefaultContributorRole.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import { ICollectionsRepository } from '../../../src/collections/domain/repositories/ICollectionsRepository'
import { WriteError } from '../../../src'
import { SetDefaultContributorRole } from '../../../src/collections/domain/useCases/SetDefaultContributorRole'
import { RoleAlias } from '../../../src/roles/domain/models/RoleAlias'

describe('execute', () => {
test('should set default contributor role on repository success', async () => {
const collectionRepositoryStub: ICollectionsRepository = {} as ICollectionsRepository
collectionRepositoryStub.setDefaultContributorRole = jest.fn().mockResolvedValue(undefined)
const testSetDefaultContributorRole = new SetDefaultContributorRole(collectionRepositoryStub)

await expect(testSetDefaultContributorRole.execute(1, RoleAlias.CURATOR)).resolves.toBeUndefined()
expect(collectionRepositoryStub.setDefaultContributorRole).toHaveBeenCalledWith(1, 'curator')
})

test('should return error result on repository error', async () => {
const collectionRepositoryStub: ICollectionsRepository = {} as ICollectionsRepository
collectionRepositoryStub.setDefaultContributorRole = jest
.fn()
.mockRejectedValue(new WriteError())
const testSetDefaultContributorRole = new SetDefaultContributorRole(collectionRepositoryStub)

await expect(testSetDefaultContributorRole.execute(1, RoleAlias.CURATOR)).rejects.toThrow(WriteError)
expect(collectionRepositoryStub.setDefaultContributorRole).toHaveBeenCalledWith(1, 'curator')
})
})
Loading