diff --git a/docs/useCases.md b/docs/useCases.md index f77a40e1..6ec7798a 100644 --- a/docs/useCases.md +++ b/docs/useCases.md @@ -25,6 +25,8 @@ 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) + - [Assign a Role on a Collection](#assign-a-role-on-a-collection) + - [Unassign a Role on a Collection](#unassign-a-role-on-a-collection) - [Templates](#Templates) - [Templates read use cases](#templates-read-use-cases) - [Get a Template](#get-a-template) @@ -63,6 +65,8 @@ The different use cases currently available in the package are classified below, - [Link Dataset Type with Metadata Blocks](#link-dataset-type-with-metadata-blocks) - [Set Available Licenses For Dataset Type](#set-available-licenses-for-dataset-type) - [Delete a Dataset Type](#delete-a-dataset-type) + - [Assign a Role on a Dataset](#assign-a-role-on-a-dataset) + - [Unassign a Role on a Dataset](#unassign-a-role-on-a-dataset) - [Files](#Files) - [Files read use cases](#files-read-use-cases) - [Get a File](#get-a-file) @@ -576,6 +580,49 @@ deleteCollectionFeaturedItem.execute(featuredItemId) _See [use case](../src/collections/domain/useCases/DeleteCollectionFeaturedItem.ts)_ definition. +#### Assign a Role on a Collection + +Assigns a role on a collection, given a collection identifier, a role assignee and a role alias. + +##### Example call: + +```typescript +import { assignRoleOnCollection } from '@iqss/dataverse-client-javascript' + +/* ... */ + +const collectionIdOrAlias = 12345 +const roleAssignee = "@myUser" +const roleAlias = "curator" + +assignRoleOnCollection.execute(collectionIdOrAlias, roleAssignee, roleAlias) + +/* ... */ +``` + +_See [use case](../src/collections/domain/useCases/AssignRoleOnCollection.ts)_ definition. + +#### Unassign a Role on a Collection + +Unassigns a role on a collection, given a collection identifier and a role assignment identifier. + +##### Example call: + +```typescript +import { unassignRoleOnCollection } from '@iqss/dataverse-client-javascript' + +/* ... */ + +const collectionIdOrAlias = 12345 +const roleAssignmentId = 67890 + +unassignRoleOnCollection.execute(collectionIdOrAlias, roleAssignmentId) + +/* ... */ +``` + +_See [use case](../src/collections/domain/useCases/UnassignRoleOnCollection.ts)_ definition. + ## Templates ### Templates Read Use Cases @@ -1516,6 +1563,49 @@ deleteDatasetType.execute(datasetTypeId).then(() => { _See [use case](../src/datasets/domain/useCases/DeleteDatasetType.ts) implementation_. +#### Assign a Role on a Dataset + +Assigns a role on a dataset, given a dataset identifier, a role assignee and a role alias. + +##### Example call: + +```typescript +import { assignRoleOnDataset } from '@iqss/dataverse-client-javascript' + +/* ... */ + +const datasetId = 1 +const roleAssignee = "@myUser" +const roleAlias = "curator" + +assignRoleOnDataset.execute(datasetId, roleAssignee, roleAlias) + +/* ... */ +``` + +_See [use case](../src/datasets/domain/useCases/AssignRoleOnDataset.ts)_ definition. + +#### Unassign a Role on a Dataset + +Unassigns a role on a dataset, given a dataset identifier and a role assignment identifier. + +##### Example call: + +```typescript +import { unassignRoleOnDataset } from '@iqss/dataverse-client-javascript' + +/* ... */ + +const datasetId = 1 +const roleAssignmentId = 67890 + +unassignRoleOnDataset.execute(datasetId, roleAssignmentId) + +/* ... */ +``` + +_See [use case](../src/datasets/domain/useCases/UnassignRoleOnDataset.ts)_ definition. + ## Files ### Files read use cases diff --git a/src/collections/domain/repositories/ICollectionsRepository.ts b/src/collections/domain/repositories/ICollectionsRepository.ts index bc8960c8..f83f639c 100644 --- a/src/collections/domain/repositories/ICollectionsRepository.ts +++ b/src/collections/domain/repositories/ICollectionsRepository.ts @@ -25,6 +25,15 @@ export interface ICollectionsRepository { getCollectionUserPermissions( collectionIdOrAlias: number | string ): Promise + assignRoleOnCollection( + collectionIdOrAlias: number | string, + roleAssignee: string, + roleAlias: string + ): Promise + unassignRoleOnCollection( + collectionIdOrAlias: number | string, + roleAssignmentId: number + ): Promise getCollectionItems( collectionId?: string, limit?: number, diff --git a/src/collections/domain/useCases/AssignRoleOnCollection.ts b/src/collections/domain/useCases/AssignRoleOnCollection.ts new file mode 100644 index 00000000..e688677d --- /dev/null +++ b/src/collections/domain/useCases/AssignRoleOnCollection.ts @@ -0,0 +1,28 @@ +import { UseCase } from '../../../core/domain/useCases/UseCase' +import { ICollectionsRepository } from '../repositories/ICollectionsRepository' +import { ROOT_COLLECTION_ID } from '../models/Collection' + +export class AssignRoleOnCollection implements UseCase { + private collectionsRepository: ICollectionsRepository + + constructor(collectionsRepository: ICollectionsRepository) { + this.collectionsRepository = collectionsRepository + } + + /** + * Assigns a new role to someone on the given Dataverse 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 {string} [roleAssignee] - To whom the role should be assigned + * @param {string} [roleAlias] - The alias of the role to be assigned + * @returns {Promise} + */ + async execute( + collectionIdOrAlias: number | string = ROOT_COLLECTION_ID, + roleAssignee: string, + roleAlias: string + ): Promise { + return await this.collectionsRepository.assignRoleOnCollection(collectionIdOrAlias, roleAssignee, roleAlias) + } +} diff --git a/src/collections/domain/useCases/UnassignRoleOnCollection.ts b/src/collections/domain/useCases/UnassignRoleOnCollection.ts new file mode 100644 index 00000000..9a752a78 --- /dev/null +++ b/src/collections/domain/useCases/UnassignRoleOnCollection.ts @@ -0,0 +1,26 @@ +import { UseCase } from '../../../core/domain/useCases/UseCase' +import { ICollectionsRepository } from '../repositories/ICollectionsRepository' +import { ROOT_COLLECTION_ID } from '../models/Collection' + +export class UnassignRoleOnCollection implements UseCase { + private collectionsRepository: ICollectionsRepository + + constructor(collectionsRepository: ICollectionsRepository) { + this.collectionsRepository = collectionsRepository + } + + /** + * Deletes the given role assignment on the given Dataverse 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 {number} [roleAssignmentId] - To numeric identifier of the role assignment + * @returns {Promise} + */ + async execute( + collectionIdOrAlias: number | string = ROOT_COLLECTION_ID, + roleAssignmentId: number + ): Promise { + return await this.collectionsRepository.unassignRoleOnCollection(collectionIdOrAlias, roleAssignmentId) + } +} diff --git a/src/collections/index.ts b/src/collections/index.ts index 59e2e50b..589abe87 100644 --- a/src/collections/index.ts +++ b/src/collections/index.ts @@ -16,6 +16,8 @@ import { LinkCollection } from './domain/useCases/LinkCollection' import { UnlinkCollection } from './domain/useCases/UnlinkCollection' import { GetCollectionLinks } from './domain/useCases/GetCollectionLinks' import { GetCollectionsForLinking } from './domain/useCases/GetCollectionsForLinking' +import { AssignRoleOnCollection } from './domain/useCases/AssignRoleOnCollection' +import { UnassignRoleOnCollection } from './domain/useCases/UnassignRoleOnCollection' const collectionsRepository = new CollectionsRepository() @@ -36,6 +38,8 @@ const linkCollection = new LinkCollection(collectionsRepository) const unlinkCollection = new UnlinkCollection(collectionsRepository) const getCollectionLinks = new GetCollectionLinks(collectionsRepository) const getCollectionsForLinking = new GetCollectionsForLinking(collectionsRepository) +const assignRoleOnCollection = new AssignRoleOnCollection(collectionsRepository) +const unassignRoleOnCollection = new UnassignRoleOnCollection(collectionsRepository) export { getCollection, @@ -54,7 +58,9 @@ export { linkCollection, unlinkCollection, getCollectionLinks, - getCollectionsForLinking + getCollectionsForLinking, + assignRoleOnCollection, + unassignRoleOnCollection } export { Collection, CollectionInputLevel } from './domain/models/Collection' export { CollectionFacet } from './domain/models/CollectionFacet' diff --git a/src/collections/infra/repositories/CollectionsRepository.ts b/src/collections/infra/repositories/CollectionsRepository.ts index e0e459b0..373623d5 100644 --- a/src/collections/infra/repositories/CollectionsRepository.ts +++ b/src/collections/infra/repositories/CollectionsRepository.ts @@ -167,6 +167,32 @@ export class CollectionsRepository extends ApiRepository implements ICollections }) } + public async assignRoleOnCollection( + collectionIdOrAlias: number | string, + roleAssignee: string, + roleAlias: string + ): Promise { + return this.doPost(`/${this.collectionsResourceName}/${collectionIdOrAlias}/assignments`, { + assignee: roleAssignee, + role: roleAlias + }) + .then(() => undefined) + .catch((error) => { + throw error + }) + } + + public async unassignRoleOnCollection( + collectionIdOrAlias: number | string, + roleAssignmentId: number + ): Promise { + return this.doDelete(`/${this.collectionsResourceName}/${collectionIdOrAlias}/assignments/${roleAssignmentId}`) + .then(() => undefined) + .catch((error) => { + throw error + }) + } + public async getCollectionItems( collectionId?: string, limit?: number, diff --git a/src/datasets/domain/repositories/IDatasetsRepository.ts b/src/datasets/domain/repositories/IDatasetsRepository.ts index 09859777..9aeced81 100644 --- a/src/datasets/domain/repositories/IDatasetsRepository.ts +++ b/src/datasets/domain/repositories/IDatasetsRepository.ts @@ -102,4 +102,13 @@ export interface IDatasetsRepository { payload: DatasetLicenseUpdateRequest ): Promise getDatasetStorageDriver(datasetId: number | string): Promise + assignRoleOnDataset( + datasetId: number | string, + roleAssignee: string, + roleAlias: string + ): Promise + unassignRoleOnDataset( + datasetId: number | string, + roleAssignmentId: number + ): Promise } diff --git a/src/datasets/domain/useCases/AssignRoleOnDataset.ts b/src/datasets/domain/useCases/AssignRoleOnDataset.ts new file mode 100644 index 00000000..92d6e849 --- /dev/null +++ b/src/datasets/domain/useCases/AssignRoleOnDataset.ts @@ -0,0 +1,26 @@ +import { UseCase } from '../../../core/domain/useCases/UseCase' +import { IDatasetsRepository } from '../repositories/IDatasetsRepository' + +export class AssignRoleOnDataset implements UseCase { + private datasetsRepository: IDatasetsRepository + + constructor(datasetsRepository: IDatasetsRepository) { + this.datasetsRepository = datasetsRepository + } + + /** + * Assigns a new role to someone on the given dataset. + * + * @param {number | string} [datasetId] - The dataset identifier, which can be a string (for persistent identifiers), or a number (for numeric identifiers). + * @param {string} [roleAssignee] - To whom the role should be assigned + * @param {string} [roleAlias] - The alias of the role to be assigned + * @returns {Promise} + */ + async execute( + datasetId: number | string, + roleAssignee: string, + roleAlias: string + ): Promise { + return await this.datasetsRepository.assignRoleOnDataset(datasetId, roleAssignee, roleAlias) + } +} diff --git a/src/datasets/domain/useCases/UnassignRoleOnDataset.ts b/src/datasets/domain/useCases/UnassignRoleOnDataset.ts new file mode 100644 index 00000000..32baf096 --- /dev/null +++ b/src/datasets/domain/useCases/UnassignRoleOnDataset.ts @@ -0,0 +1,24 @@ +import { UseCase } from '../../../core/domain/useCases/UseCase' +import { IDatasetsRepository } from '../repositories/IDatasetsRepository' + +export class UnassignRoleOnDataset implements UseCase { + private datasetsRepository: IDatasetsRepository + + constructor(datasetsRepository: IDatasetsRepository) { + this.datasetsRepository = datasetsRepository + } + + /** + * Deletes the given role assignment on the given dataset. + * + * @param {number | string} [datasetId] - The dataset identifier, which can be a string (for persistent identifiers), or a number (for numeric identifiers). + * @param {number} [roleAssignmentId] - To numeric identifier of the role assignment + * @returns {Promise} + */ + async execute( + datasetId: number | string, + roleAssignmentId: number + ): Promise { + return await this.datasetsRepository.unassignRoleOnDataset(datasetId, roleAssignmentId) + } +} diff --git a/src/datasets/index.ts b/src/datasets/index.ts index 3c081fc6..9ad0fc3c 100644 --- a/src/datasets/index.ts +++ b/src/datasets/index.ts @@ -34,6 +34,8 @@ import { GetDatasetCitationInOtherFormats } from './domain/useCases/GetDatasetCi import { UpdateTermsOfAccess } from './domain/useCases/UpdateTermsOfAccess' import { UpdateDatasetLicense } from './domain/useCases/UpdateDatasetLicense' import { GetDatasetStorageDriver } from './domain/useCases/GetDatasetStorageDriver' +import { AssignRoleOnDataset } from './domain/useCases/AssignRoleOnDataset' +import { UnassignRoleOnDataset } from './domain/useCases/UnassignRoleOnDataset' const datasetsRepository = new DatasetsRepository() @@ -84,6 +86,8 @@ const getDatasetCitationInOtherFormats = new GetDatasetCitationInOtherFormats(da const updateTermsOfAccess = new UpdateTermsOfAccess(datasetsRepository) const updateDatasetLicense = new UpdateDatasetLicense(datasetsRepository) const getDatasetStorageDriver = new GetDatasetStorageDriver(datasetsRepository) +const assignRoleOnDataset = new AssignRoleOnDataset(datasetsRepository) +const unassignRoleOnDataset = new UnassignRoleOnDataset(datasetsRepository) export { getDataset, @@ -115,7 +119,9 @@ export { setAvailableLicensesForDatasetType, deleteDatasetType, updateDatasetLicense, - getDatasetStorageDriver + getDatasetStorageDriver, + assignRoleOnDataset, + unassignRoleOnDataset } export { DatasetNotNumberedVersion } from './domain/models/DatasetNotNumberedVersion' export { DatasetUserPermissions } from './domain/models/DatasetUserPermissions' diff --git a/src/datasets/infra/repositories/DatasetsRepository.ts b/src/datasets/infra/repositories/DatasetsRepository.ts index c29db6c0..49230662 100644 --- a/src/datasets/infra/repositories/DatasetsRepository.ts +++ b/src/datasets/infra/repositories/DatasetsRepository.ts @@ -511,4 +511,30 @@ export class DatasetsRepository extends ApiRepository implements IDatasetsReposi throw error }) } + + public async assignRoleOnDataset( + datasetId: number | string, + roleAssignee: string, + roleAlias: string + ): Promise { + return this.doPost(this.buildApiEndpoint(this.datasetsResourceName, 'assignments', datasetId), { + assignee: roleAssignee, + role: roleAlias + }) + .then(() => undefined) + .catch((error) => { + throw error + }) + } + + public async unassignRoleOnDataset( + datasetId: number | string, + roleAssignmentId: number + ): Promise { + return this.doDelete(this.buildApiEndpoint(this.datasetsResourceName, `assignments/${roleAssignmentId}`, datasetId)) + .then(() => undefined) + .catch((error) => { + throw error + }) + } } diff --git a/test/unit/collections/AssignRoleOnCollection.test.ts b/test/unit/collections/AssignRoleOnCollection.test.ts new file mode 100644 index 00000000..255dc08d --- /dev/null +++ b/test/unit/collections/AssignRoleOnCollection.test.ts @@ -0,0 +1,25 @@ +import { ICollectionsRepository } from '../../../src/collections/domain/repositories/ICollectionsRepository' +import { WriteError } from '../../../src' +import { AssignRoleOnCollection } from '../../../src/collections/domain/useCases/AssignRoleOnCollection' + +describe('execute', () => { + test('should assign role successfully on repository success', async () => { + const collectionRepositoryStub: ICollectionsRepository = {} as ICollectionsRepository + collectionRepositoryStub.assignRoleOnCollection = jest.fn().mockResolvedValue(undefined) + + const testAssignRoleOnCollection = new AssignRoleOnCollection(collectionRepositoryStub) + + await expect(testAssignRoleOnCollection.execute(1, "@testUser", "curator")).resolves.toBeUndefined() + expect(collectionRepositoryStub.assignRoleOnCollection).toHaveBeenCalledWith(1, "@testUser", "curator") + }) + + test('should throw error on repository failure', async () => { + const collectionRepositoryStub: ICollectionsRepository = {} as ICollectionsRepository + collectionRepositoryStub.assignRoleOnCollection = jest.fn().mockRejectedValue(new WriteError()) + + const testAssignRoleOnCollection = new AssignRoleOnCollection(collectionRepositoryStub) + + await expect(testAssignRoleOnCollection.execute(1, "@testUser", "curator")).rejects.toThrow(WriteError) + expect(collectionRepositoryStub.assignRoleOnCollection).toHaveBeenCalledWith(1, "@testUser", "curator") + }) +}) diff --git a/test/unit/collections/UnassignRoleOnCollection.test.ts b/test/unit/collections/UnassignRoleOnCollection.test.ts new file mode 100644 index 00000000..393f8fef --- /dev/null +++ b/test/unit/collections/UnassignRoleOnCollection.test.ts @@ -0,0 +1,25 @@ +import { ICollectionsRepository } from '../../../src/collections/domain/repositories/ICollectionsRepository' +import { WriteError } from '../../../src' +import { UnassignRoleOnCollection } from '../../../src/collections/domain/useCases/UnassignRoleOnCollection' + +describe('execute', () => { + test('should unassign role successfully on repository success', async () => { + const collectionRepositoryStub: ICollectionsRepository = {} as ICollectionsRepository + collectionRepositoryStub.unassignRoleOnCollection = jest.fn().mockResolvedValue(undefined) + + const testUnassignRoleOnCollection = new UnassignRoleOnCollection(collectionRepositoryStub) + + await expect(testUnassignRoleOnCollection.execute(1, 2)).resolves.toBeUndefined() + expect(collectionRepositoryStub.unassignRoleOnCollection).toHaveBeenCalledWith(1, 2) + }) + + test('should throw error on repository failure', async () => { + const collectionRepositoryStub: ICollectionsRepository = {} as ICollectionsRepository + collectionRepositoryStub.unassignRoleOnCollection = jest.fn().mockRejectedValue(new WriteError()) + + const testUnassignRoleOnCollection = new UnassignRoleOnCollection(collectionRepositoryStub) + + await expect(testUnassignRoleOnCollection.execute(1, 2)).rejects.toThrow(WriteError) + expect(collectionRepositoryStub.unassignRoleOnCollection).toHaveBeenCalledWith(1, 2) + }) +}) diff --git a/test/unit/datasets/AssignRoleOnDataset.test.ts b/test/unit/datasets/AssignRoleOnDataset.test.ts new file mode 100644 index 00000000..bea50ab7 --- /dev/null +++ b/test/unit/datasets/AssignRoleOnDataset.test.ts @@ -0,0 +1,25 @@ +import { IDatasetsRepository } from '../../../src/datasets/domain/repositories/IDatasetsRepository' +import { WriteError } from '../../../src' +import { AssignRoleOnDataset } from '../../../src/datasets/domain/useCases/AssignRoleOnDataset' + +describe('execute', () => { + test('should assign role successfully on repository success', async () => { + const datasetsRepositoryStub: IDatasetsRepository = {} as IDatasetsRepository + datasetsRepositoryStub.assignRoleOnDataset = jest.fn().mockResolvedValue(undefined) + + const testAssignRoleOnDataset = new AssignRoleOnDataset(datasetsRepositoryStub) + + await expect(testAssignRoleOnDataset.execute(1, "@testUser", "curator")).resolves.toBeUndefined() + expect(datasetsRepositoryStub.assignRoleOnDataset).toHaveBeenCalledWith(1, "@testUser", "curator") + }) + + test('should throw error on repository failure', async () => { + const datasetsRepositoryStub: IDatasetsRepository = {} as IDatasetsRepository + datasetsRepositoryStub.assignRoleOnDataset = jest.fn().mockRejectedValue(new WriteError()) + + const testAssignRoleOnDataset = new AssignRoleOnDataset(datasetsRepositoryStub) + + await expect(testAssignRoleOnDataset.execute(1, "@testUser", "curator")).rejects.toThrow(WriteError) + expect(datasetsRepositoryStub.assignRoleOnDataset).toHaveBeenCalledWith(1, "@testUser", "curator") + }) +}) diff --git a/test/unit/datasets/UnassignRoleOnDataset.test.ts b/test/unit/datasets/UnassignRoleOnDataset.test.ts new file mode 100644 index 00000000..a168f6ab --- /dev/null +++ b/test/unit/datasets/UnassignRoleOnDataset.test.ts @@ -0,0 +1,25 @@ +import { IDatasetsRepository } from '../../../src/datasets/domain/repositories/IDatasetsRepository' +import { WriteError } from '../../../src' +import { UnassignRoleOnDataset } from '../../../src/datasets/domain/useCases/UnassignRoleOnDataset' + +describe('execute', () => { + test('should unassign role successfully on repository success', async () => { + const datasetsRepositoryStub: IDatasetsRepository = {} as IDatasetsRepository + datasetsRepositoryStub.unassignRoleOnDataset = jest.fn().mockResolvedValue(undefined) + + const testUnassignRoleOnDataset = new UnassignRoleOnDataset(datasetsRepositoryStub) + + await expect(testUnassignRoleOnDataset.execute(1, 2)).resolves.toBeUndefined() + expect(datasetsRepositoryStub.unassignRoleOnDataset).toHaveBeenCalledWith(1, 2) + }) + + test('should throw error on repository failure', async () => { + const datasetsRepositoryStub: IDatasetsRepository = {} as IDatasetsRepository + datasetsRepositoryStub.unassignRoleOnDataset = jest.fn().mockRejectedValue(new WriteError()) + + const testUnassignRoleOnDataset = new UnassignRoleOnDataset(datasetsRepositoryStub) + + await expect(testUnassignRoleOnDataset.execute(1, 2)).rejects.toThrow(WriteError) + expect(datasetsRepositoryStub.unassignRoleOnDataset).toHaveBeenCalledWith(1, 2) + }) +})