From 88889823a4504403d52dbfe0d95879fa01c92b5b Mon Sep 17 00:00:00 2001 From: Philip Durbin Date: Mon, 22 Sep 2025 16:14:47 -0400 Subject: [PATCH 1/3] add, edit, and delete dataset types #370 --- docs/useCases.md | 95 ++++++++++++++++++ src/datasets/domain/models/DatasetType.ts | 2 +- .../repositories/IDatasetsRepository.ts | 11 +++ .../domain/useCases/AddDatasetType.ts | 18 ++++ .../domain/useCases/DeleteDatasetType.ts | 17 ++++ .../GetDatasetAvailableDatasetType.ts | 18 ++++ .../LinkDatasetTypeWithMetadataBlocks.ts | 20 ++++ .../SetAvailableLicensesForDatasetType.ts | 17 ++++ src/datasets/index.ts | 19 +++- .../infra/repositories/DatasetsRepository.ts | 66 +++++++++++++ .../datasets/AddDatasetType.test.ts | 28 ++++++ .../datasets/DeleteDatasetType.test.ts | 28 ++++++ .../GetDatasetAvailableDatasetType.test.ts | 30 ++++++ .../LinkDatasetTypeWithMetadataBlocks.test.ts | 40 ++++++++ ...SetAvailableLicensesForDatasetType.test.ts | 40 ++++++++ .../datasets/DatasetsRepository.test.ts | 96 ++++++++++++++++++- test/unit/datasets/DeleteDatasetType.test.ts | 23 +++++ .../GetDatasetAvailableDatasetType.test.ts | 54 +++++++++++ .../LinkDatasetTypeWithMetadataBlocks.test.ts | 27 ++++++ ...SetAvailableLicensesForDatasetType.test.ts | 27 ++++++ 20 files changed, 673 insertions(+), 3 deletions(-) create mode 100644 src/datasets/domain/useCases/AddDatasetType.ts create mode 100644 src/datasets/domain/useCases/DeleteDatasetType.ts create mode 100644 src/datasets/domain/useCases/GetDatasetAvailableDatasetType.ts create mode 100644 src/datasets/domain/useCases/LinkDatasetTypeWithMetadataBlocks.ts create mode 100644 src/datasets/domain/useCases/SetAvailableLicensesForDatasetType.ts create mode 100644 test/functional/datasets/AddDatasetType.test.ts create mode 100644 test/functional/datasets/DeleteDatasetType.test.ts create mode 100644 test/functional/datasets/GetDatasetAvailableDatasetType.test.ts create mode 100644 test/functional/datasets/LinkDatasetTypeWithMetadataBlocks.test.ts create mode 100644 test/functional/datasets/SetAvailableLicensesForDatasetType.test.ts create mode 100644 test/unit/datasets/DeleteDatasetType.test.ts create mode 100644 test/unit/datasets/GetDatasetAvailableDatasetType.test.ts create mode 100644 test/unit/datasets/LinkDatasetTypeWithMetadataBlocks.test.ts create mode 100644 test/unit/datasets/SetAvailableLicensesForDatasetType.test.ts diff --git a/docs/useCases.md b/docs/useCases.md index 2c5e9b5a..ece5c722 100644 --- a/docs/useCases.md +++ b/docs/useCases.md @@ -40,6 +40,7 @@ The different use cases currently available in the package are classified below, - [Get Dataset Available Categories](#get-dataset-available-categories) - [Get Dataset Templates](#get-dataset-templates) - [Get Dataset Available Dataset Types](#get-dataset-available-dataset-types) + - [Get Dataset Available Dataset Type](#get-dataset-available-dataset-type) - [Datasets write use cases](#datasets-write-use-cases) - [Create a Dataset](#create-a-dataset) - [Update a Dataset](#update-a-dataset) @@ -48,6 +49,10 @@ The different use cases currently available in the package are classified below, - [Delete a Draft Dataset](#delete-a-draft-dataset) - [Link a Dataset](#link-a-dataset) - [Unlink a Dataset](#unlink-a-dataset) + - [Add a Dataset Type](#add-a-dataset-type) + - [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) - [Files](#Files) - [Files read use cases](#files-read-use-cases) - [Get a File](#get-a-file) @@ -833,6 +838,24 @@ getDatasetAvailableDatasetTypes.execute().then((datasetTypes: DatasetType[]) => _See [use case](../src/datasets/domain/useCases/GetDatasetAvailableDatasetTypes.ts) implementation_. +#### Get Dataset Available Dataset Type + +Returns an available dataset types that can be used at dataset creation. + +###### Example call: + +```typescript +import { getDatasetAvailableDatasetType } from '@iqss/dataverse-client-javascript' + +/* ... */ + +getDatasetAvailableDatasetType.execute().then((datasetType: DatasetType) => { + /* ... */ +}) +``` + +_See [use case](../src/datasets/domain/useCases/GetDatasetAvailableDatasetType.ts) implementation_. + ### Datasets Write Use Cases #### Create a Dataset @@ -1156,6 +1179,78 @@ getDatasetTemplates.execute(collectionIdOrAlias).then((datasetTemplates: Dataset _See [use case](../src/datasets/domain/useCases/GetDatasetTemplates.ts)_ definition. +#### Add a Dataset Type + +Adds a dataset types that can be used at dataset creation. + +###### Example call: + +```typescript +import { addDatasetType } from '@iqss/dataverse-client-javascript' + +/* ... */ + +addDatasetType.execute(datasetType).then((datasetType: DatasetType) => { + /* ... */ +}) +``` + +_See [use case](../src/datasets/domain/useCases/AddDatasetType.ts) implementation_. + +#### Link Dataset Type with Metadata Blocks + +Link a dataset type with metadata blocks. + +###### Example call: + +```typescript +import { linkDatasetTypeWithMetadataBlocks } from '@iqss/dataverse-client-javascript' + +/* ... */ + +linkDatasetTypeWithMetadataBlocks.execute(datasetTypeId, ["geospatial"]).then(() => { + /* ... */ +}) +``` + +_See [use case](../src/datasets/domain/useCases/LinkDatasetTypeWithMetadataBlocks.ts) implementation_. + +#### Set Available Licenses For Dataset Type + +Set available licenses for dataset type. + +###### Example call: + +```typescript +import { setAvailableLicensesForDatasetType } from '@iqss/dataverse-client-javascript' + +/* ... */ + +setAvailableLicensesForDatasetType.execute(datasetTypeId, ["CC BY 4.0"]).then(() => { + /* ... */ +}) +``` + +_See [use case](../src/datasets/domain/useCases/SetAvailableLicensesForDatasetType.ts) implementation_. + +#### Delete a Dataset Type + +Delete a dataset type. + +###### Example call: + +```typescript +import { deleteDatasetType } from '@iqss/dataverse-client-javascript' + +/* ... */ + +deleteDatasetType.execute(datasetTypeId).then(() => { + /* ... */ +}) +``` + +_See [use case](../src/datasets/domain/useCases/DeleteDatasetType.ts) implementation_. + ## Files ### Files read use cases diff --git a/src/datasets/domain/models/DatasetType.ts b/src/datasets/domain/models/DatasetType.ts index 5475cdaf..56a5ed43 100644 --- a/src/datasets/domain/models/DatasetType.ts +++ b/src/datasets/domain/models/DatasetType.ts @@ -1,5 +1,5 @@ export interface DatasetType { - id: number + id?: number name: string linkedMetadataBlocks?: string[] availableLicenses?: string[] diff --git a/src/datasets/domain/repositories/IDatasetsRepository.ts b/src/datasets/domain/repositories/IDatasetsRepository.ts index fb8b26d5..3fe1c7ab 100644 --- a/src/datasets/domain/repositories/IDatasetsRepository.ts +++ b/src/datasets/domain/repositories/IDatasetsRepository.ts @@ -78,4 +78,15 @@ export interface IDatasetsRepository { ): Promise getDatasetTemplates(collectionIdOrAlias: number | string): Promise getDatasetAvailableDatasetTypes(): Promise + getDatasetAvailableDatasetType(datasetTypeId: number | string): Promise + addDatasetType(datasetType: DatasetType): Promise + linkDatasetTypeWithMetadataBlocks( + datasetTypeId: number | string, + metadataBlocks: string[] + ): Promise + setAvailableLicensesForDatasetType( + datasetTypeId: number | string, + licenses: string[] + ): Promise + deleteDatasetType(datasetTypeId: number): Promise } diff --git a/src/datasets/domain/useCases/AddDatasetType.ts b/src/datasets/domain/useCases/AddDatasetType.ts new file mode 100644 index 00000000..7e2e11c9 --- /dev/null +++ b/src/datasets/domain/useCases/AddDatasetType.ts @@ -0,0 +1,18 @@ +import { UseCase } from '../../../core/domain/useCases/UseCase' +import { DatasetType } from '../models/DatasetType' +import { IDatasetsRepository } from '../repositories/IDatasetsRepository' + +export class AddDatasetType implements UseCase { + private datasetsRepository: IDatasetsRepository + + constructor(datasetsRepository: IDatasetsRepository) { + this.datasetsRepository = datasetsRepository + } + + /** + * Add a dataset type that can be selected when creating a dataset. + */ + async execute(datasetType: DatasetType): Promise { + return await this.datasetsRepository.addDatasetType(datasetType) + } +} diff --git a/src/datasets/domain/useCases/DeleteDatasetType.ts b/src/datasets/domain/useCases/DeleteDatasetType.ts new file mode 100644 index 00000000..b3c841aa --- /dev/null +++ b/src/datasets/domain/useCases/DeleteDatasetType.ts @@ -0,0 +1,17 @@ +import { UseCase } from '../../../core/domain/useCases/UseCase' +import { IDatasetsRepository } from '../repositories/IDatasetsRepository' + +export class DeleteDatasetType implements UseCase { + private datasetsRepository: IDatasetsRepository + + constructor(datasetsRepository: IDatasetsRepository) { + this.datasetsRepository = datasetsRepository + } + + /** + * Deletes a dataset type. + */ + async execute(datasetTypeId: number): Promise { + return await this.datasetsRepository.deleteDatasetType(datasetTypeId) + } +} diff --git a/src/datasets/domain/useCases/GetDatasetAvailableDatasetType.ts b/src/datasets/domain/useCases/GetDatasetAvailableDatasetType.ts new file mode 100644 index 00000000..b870216c --- /dev/null +++ b/src/datasets/domain/useCases/GetDatasetAvailableDatasetType.ts @@ -0,0 +1,18 @@ +import { UseCase } from '../../../core/domain/useCases/UseCase' +import { DatasetType } from '../models/DatasetType' +import { IDatasetsRepository } from '../repositories/IDatasetsRepository' + +export class GetDatasetAvailableDatasetType implements UseCase { + private datasetsRepository: IDatasetsRepository + + constructor(datasetsRepository: IDatasetsRepository) { + this.datasetsRepository = datasetsRepository + } + + /** + * Returns a single available dataset type that can be selected when creating a dataset. + */ + async execute(datasetTypeId: number | string): Promise { + return await this.datasetsRepository.getDatasetAvailableDatasetType(datasetTypeId) + } +} diff --git a/src/datasets/domain/useCases/LinkDatasetTypeWithMetadataBlocks.ts b/src/datasets/domain/useCases/LinkDatasetTypeWithMetadataBlocks.ts new file mode 100644 index 00000000..f9a6b447 --- /dev/null +++ b/src/datasets/domain/useCases/LinkDatasetTypeWithMetadataBlocks.ts @@ -0,0 +1,20 @@ +import { UseCase } from '../../../core/domain/useCases/UseCase' +import { IDatasetsRepository } from '../repositories/IDatasetsRepository' + +export class LinkDatasetTypeWithMetadataBlocks implements UseCase { + private datasetsRepository: IDatasetsRepository + + constructor(datasetsRepository: IDatasetsRepository) { + this.datasetsRepository = datasetsRepository + } + + /** + * Links a dataset type with one or more metadata blocks. These metadata blocks will be shown when creating a dataset of this type. + */ + async execute(datasetTypeId: number | string, metadataBlocks: string[]): Promise { + return await this.datasetsRepository.linkDatasetTypeWithMetadataBlocks( + datasetTypeId, + metadataBlocks + ) + } +} diff --git a/src/datasets/domain/useCases/SetAvailableLicensesForDatasetType.ts b/src/datasets/domain/useCases/SetAvailableLicensesForDatasetType.ts new file mode 100644 index 00000000..e4df9ab2 --- /dev/null +++ b/src/datasets/domain/useCases/SetAvailableLicensesForDatasetType.ts @@ -0,0 +1,17 @@ +import { UseCase } from '../../../core/domain/useCases/UseCase' +import { IDatasetsRepository } from '../repositories/IDatasetsRepository' + +export class SetAvailableLicensesForDatasetType implements UseCase { + private datasetsRepository: IDatasetsRepository + + constructor(datasetsRepository: IDatasetsRepository) { + this.datasetsRepository = datasetsRepository + } + + /** + * Sets the available licenses for a given dataset type. This limits the license options when creating a dataset of this type. + */ + async execute(datasetTypeId: number | string, licenses: string[]): Promise { + return await this.datasetsRepository.setAvailableLicensesForDatasetType(datasetTypeId, licenses) + } +} diff --git a/src/datasets/index.ts b/src/datasets/index.ts index e6de1b8c..6b93a7cd 100644 --- a/src/datasets/index.ts +++ b/src/datasets/index.ts @@ -25,6 +25,11 @@ import { UnlinkDataset } from './domain/useCases/UnlinkDataset' import { GetDatasetLinkedCollections } from './domain/useCases/GetDatasetLinkedCollections' import { GetDatasetAvailableCategories } from './domain/useCases/GetDatasetAvailableCategories' import { GetDatasetAvailableDatasetTypes } from './domain/useCases/GetDatasetAvailableDatasetTypes' +import { GetDatasetAvailableDatasetType } from './domain/useCases/GetDatasetAvailableDatasetType' +import { AddDatasetType } from './domain/useCases/AddDatasetType' +import { LinkDatasetTypeWithMetadataBlocks } from './domain/useCases/LinkDatasetTypeWithMetadataBlocks' +import { SetAvailableLicensesForDatasetType } from './domain/useCases/SetAvailableLicensesForDatasetType' +import { DeleteDatasetType } from './domain/useCases/DeleteDatasetType' import { GetDatasetCitationInOtherFormats } from './domain/useCases/GetDatasetCitationInOtherFormats' import { GetDatasetTemplates } from './domain/useCases/GetDatasetTemplates' @@ -66,6 +71,13 @@ const unlinkDataset = new UnlinkDataset(datasetsRepository) const getDatasetLinkedCollections = new GetDatasetLinkedCollections(datasetsRepository) const getDatasetAvailableCategories = new GetDatasetAvailableCategories(datasetsRepository) const getDatasetAvailableDatasetTypes = new GetDatasetAvailableDatasetTypes(datasetsRepository) +const getDatasetAvailableDatasetType = new GetDatasetAvailableDatasetType(datasetsRepository) +const addDatasetType = new AddDatasetType(datasetsRepository) +const linkDatasetTypeWithMetadataBlocks = new LinkDatasetTypeWithMetadataBlocks(datasetsRepository) +const setAvailableLicensesForDatasetType = new SetAvailableLicensesForDatasetType( + datasetsRepository +) +const deleteDatasetType = new DeleteDatasetType(datasetsRepository) const getDatasetCitationInOtherFormats = new GetDatasetCitationInOtherFormats(datasetsRepository) const getDatasetTemplates = new GetDatasetTemplates(datasetsRepository) @@ -92,7 +104,12 @@ export { getDatasetAvailableCategories, getDatasetCitationInOtherFormats, getDatasetTemplates, - getDatasetAvailableDatasetTypes + getDatasetAvailableDatasetTypes, + getDatasetAvailableDatasetType, + addDatasetType, + linkDatasetTypeWithMetadataBlocks, + setAvailableLicensesForDatasetType, + deleteDatasetType } 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 594744b4..99b380df 100644 --- a/src/datasets/infra/repositories/DatasetsRepository.ts +++ b/src/datasets/infra/repositories/DatasetsRepository.ts @@ -382,4 +382,70 @@ export class DatasetsRepository extends ApiRepository implements IDatasetsReposi throw error }) } + + public async getDatasetAvailableDatasetType( + datasetTypeId: number | string + ): Promise { + const endpoint = this.buildApiEndpoint( + this.datasetsResourceName, + 'datasetTypes/' + datasetTypeId + ) + return this.doGet(endpoint) + .then((response) => response.data.data) + .catch((error) => { + throw error + }) + } + + public async addDatasetType(datasetType: DatasetType): Promise { + return this.doPost( + this.buildApiEndpoint(this.datasetsResourceName, 'datasetTypes'), + datasetType + ) + .then((response) => response.data.data) + .catch((error) => { + throw error + }) + } + + public async linkDatasetTypeWithMetadataBlocks( + datasetTypeId: number | string, + metadataBlocks: string[] + ): Promise { + return this.doPut( + this.buildApiEndpoint(this.datasetsResourceName, 'datasetTypes/' + datasetTypeId), + metadataBlocks + ) + .then((response) => response.data.data) + .catch((error) => { + throw error + }) + } + + public async setAvailableLicensesForDatasetType( + datasetTypeId: number | string, + licenses: string[] + ): Promise { + return this.doPut( + this.buildApiEndpoint( + this.datasetsResourceName, + 'datasetTypes/' + datasetTypeId + '/licenses' + ), + licenses + ) + .then((response) => response.data.data) + .catch((error) => { + throw error + }) + } + + public async deleteDatasetType(datasetTypeId: number): Promise { + return this.doDelete( + this.buildApiEndpoint(this.datasetsResourceName, 'datasetTypes/' + datasetTypeId) + ) + .then((response) => response.data.data) + .catch((error) => { + throw error + }) + } } diff --git a/test/functional/datasets/AddDatasetType.test.ts b/test/functional/datasets/AddDatasetType.test.ts new file mode 100644 index 00000000..409c18b9 --- /dev/null +++ b/test/functional/datasets/AddDatasetType.test.ts @@ -0,0 +1,28 @@ +import { ApiConfig, DatasetType, addDatasetType, deleteDatasetType } from '../../../src' +import { DataverseApiAuthMechanism } from '../../../src/core/infra/repositories/ApiConfig' +import { TestConstants } from '../../testHelpers/TestConstants' + +describe('AddDatasetType', () => { + describe('execute', () => { + beforeAll(async () => { + ApiConfig.init( + TestConstants.TEST_API_URL, + DataverseApiAuthMechanism.API_KEY, + process.env.TEST_API_KEY + ) + }) + + test('should allow for adding and deleting a dataset type', async () => { + const randomName = `datasetType-${crypto.randomUUID().slice(0, 6)}` + const actual: DatasetType = await addDatasetType.execute({ + name: randomName, + linkedMetadataBlocks: [], + availableLicenses: [] + }) + expect(actual.name).toEqual(randomName) + + const deleted: void = await deleteDatasetType.execute(actual.id as number) + expect(deleted).toEqual({ message: 'deleted' }) + }) + }) +}) diff --git a/test/functional/datasets/DeleteDatasetType.test.ts b/test/functional/datasets/DeleteDatasetType.test.ts new file mode 100644 index 00000000..8f447822 --- /dev/null +++ b/test/functional/datasets/DeleteDatasetType.test.ts @@ -0,0 +1,28 @@ +import { ApiConfig, DatasetType, addDatasetType, deleteDatasetType } from '../../../src' +import { DataverseApiAuthMechanism } from '../../../src/core/infra/repositories/ApiConfig' +import { TestConstants } from '../../testHelpers/TestConstants' + +describe('DeleteDatasetType', () => { + describe('execute', () => { + beforeAll(async () => { + ApiConfig.init( + TestConstants.TEST_API_URL, + DataverseApiAuthMechanism.API_KEY, + process.env.TEST_API_KEY + ) + }) + + test('should allow for adding and deleting a dataset type', async () => { + const randomName = `datasetType-${crypto.randomUUID().slice(0, 6)}` + const actual: DatasetType = await addDatasetType.execute({ + name: randomName, + linkedMetadataBlocks: [], + availableLicenses: [] + }) + expect(actual.name).toEqual(randomName) + + const deleted: void = await deleteDatasetType.execute(actual.id as number) + expect(deleted).toEqual({ message: 'deleted' }) + }) + }) +}) diff --git a/test/functional/datasets/GetDatasetAvailableDatasetType.test.ts b/test/functional/datasets/GetDatasetAvailableDatasetType.test.ts new file mode 100644 index 00000000..3d80f35a --- /dev/null +++ b/test/functional/datasets/GetDatasetAvailableDatasetType.test.ts @@ -0,0 +1,30 @@ +import { ApiConfig, DatasetType, getDatasetAvailableDatasetType } from '../../../src' +import { DataverseApiAuthMechanism } from '../../../src/core/infra/repositories/ApiConfig' +import { TestConstants } from '../../testHelpers/TestConstants' + +describe('getDatasetAvailableDatasetType', () => { + describe('execute', () => { + beforeAll(async () => { + ApiConfig.init( + TestConstants.TEST_API_URL, + DataverseApiAuthMechanism.API_KEY, + process.env.TEST_API_KEY + ) + }) + + test('should return the default available dataset type', async () => { + const defaultDatasetType = 'dataset' + const actualDatasetType: DatasetType = await getDatasetAvailableDatasetType.execute( + defaultDatasetType + ) + const expectedDatasetType = { + id: 1, + name: 'dataset', + linkedMetadataBlocks: [], + availableLicenses: [] + } + + expect(actualDatasetType).toEqual(expectedDatasetType) + }) + }) +}) diff --git a/test/functional/datasets/LinkDatasetTypeWithMetadataBlocks.test.ts b/test/functional/datasets/LinkDatasetTypeWithMetadataBlocks.test.ts new file mode 100644 index 00000000..ee10efc1 --- /dev/null +++ b/test/functional/datasets/LinkDatasetTypeWithMetadataBlocks.test.ts @@ -0,0 +1,40 @@ +import { + ApiConfig, + DatasetType, + addDatasetType, + linkDatasetTypeWithMetadataBlocks +} from '../../../src' +import { DataverseApiAuthMechanism } from '../../../src/core/infra/repositories/ApiConfig' +import { TestConstants } from '../../testHelpers/TestConstants' + +describe('LinkDatasetTypeWithMetadataBlocks', () => { + describe('execute', () => { + beforeAll(async () => { + ApiConfig.init( + TestConstants.TEST_API_URL, + DataverseApiAuthMechanism.API_KEY, + process.env.TEST_API_KEY + ) + }) + + test('should allow for linking a dataset type to metadata blocks', async () => { + const randomName = `datasetType-${crypto.randomUUID().slice(0, 6)}` + const actual: DatasetType = await addDatasetType.execute({ + name: randomName, + linkedMetadataBlocks: [], + availableLicenses: [] + }) + expect(actual.name).toEqual(randomName) + + const linked: void = await linkDatasetTypeWithMetadataBlocks.execute(actual.id as number, [ + 'geospatial' + ]) + expect(linked).toEqual({ + linkedMetadataBlocks: { + before: [], + after: ['geospatial'] + } + }) + }) + }) +}) diff --git a/test/functional/datasets/SetAvailableLicensesForDatasetType.test.ts b/test/functional/datasets/SetAvailableLicensesForDatasetType.test.ts new file mode 100644 index 00000000..fdad0d2c --- /dev/null +++ b/test/functional/datasets/SetAvailableLicensesForDatasetType.test.ts @@ -0,0 +1,40 @@ +import { + ApiConfig, + DatasetType, + addDatasetType, + setAvailableLicensesForDatasetType +} from '../../../src' +import { DataverseApiAuthMechanism } from '../../../src/core/infra/repositories/ApiConfig' +import { TestConstants } from '../../testHelpers/TestConstants' + +describe('SetAvailableLicensesForDatasetType', () => { + describe('execute', () => { + beforeAll(async () => { + ApiConfig.init( + TestConstants.TEST_API_URL, + DataverseApiAuthMechanism.API_KEY, + process.env.TEST_API_KEY + ) + }) + + test('should allow for setting available licenses for a dataset type', async () => { + const randomName = `datasetType-${crypto.randomUUID().slice(0, 6)}` + const actual: DatasetType = await addDatasetType.execute({ + name: randomName, + linkedMetadataBlocks: [], + availableLicenses: [] + }) + expect(actual.name).toEqual(randomName) + + const linked: void = await setAvailableLicensesForDatasetType.execute(actual.id as number, [ + 'CC BY 4.0' + ]) + expect(linked).toEqual({ + availableLicenses: { + before: [], + after: ['CC BY 4.0'] + } + }) + }) + }) +}) diff --git a/test/integration/datasets/DatasetsRepository.test.ts b/test/integration/datasets/DatasetsRepository.test.ts index bb44f3d2..5374b5f8 100644 --- a/test/integration/datasets/DatasetsRepository.test.ts +++ b/test/integration/datasets/DatasetsRepository.test.ts @@ -22,7 +22,12 @@ import { DatasetDeaccessionDTO, publishDataset, DatasetType, - getDatasetAvailableDatasetTypes + getDatasetAvailableDatasetTypes, + getDatasetAvailableDatasetType, + addDatasetType, + deleteDatasetType, + linkDatasetTypeWithMetadataBlocks, + setAvailableLicensesForDatasetType } from '../../../src/datasets' import { ApiConfig, WriteError } from '../../../src' import { DataverseApiAuthMechanism } from '../../../src/core/infra/repositories/ApiConfig' @@ -1706,4 +1711,93 @@ describe('DatasetsRepository', () => { expect(actualDatasetTypes).toEqual(expectedDatasetTypes) }) }) + + describe('getDatasetAvailableDatasetType', () => { + test('should return available the default dataset type', async () => { + const defaultDatasetType = 'dataset' + const actualDatasetType: DatasetType = await getDatasetAvailableDatasetType.execute( + defaultDatasetType + ) + const expectedDatasetType = { + id: 1, + name: 'dataset', + linkedMetadataBlocks: [], + availableLicenses: [] + } + + expect(actualDatasetType).toEqual(expectedDatasetType) + }) + }) + + describe('addDatasetType', () => { + test('should add a dataset type', async () => { + const randomName = `datasetType-${crypto.randomUUID().slice(0, 6)}` + const actual: DatasetType = await addDatasetType.execute({ + name: randomName, + linkedMetadataBlocks: [], + availableLicenses: [] + }) + + expect(actual.name).toEqual(randomName) + }) + }) + + describe('deleteDatasetType', () => { + test('should delete a dataset type (after adding it)', async () => { + const randomName = `datasetType-${crypto.randomUUID().slice(0, 6)}` + const actual: DatasetType = await addDatasetType.execute({ + name: randomName, + linkedMetadataBlocks: [], + availableLicenses: [] + }) + expect(actual.name).toEqual(randomName) + + const deleted: void = await deleteDatasetType.execute(actual.id as number) + expect(deleted).toEqual({ message: 'deleted' }) + }) + }) + + describe('linkDatasetTypeWithMetadataBlocks', () => { + test('should allow for linking a dataset type to metadata blocks', async () => { + const randomName = `datasetType-${crypto.randomUUID().slice(0, 6)}` + const actual: DatasetType = await addDatasetType.execute({ + name: randomName, + linkedMetadataBlocks: [], + availableLicenses: [] + }) + expect(actual.name).toEqual(randomName) + + const linked: void = await linkDatasetTypeWithMetadataBlocks.execute(actual.id as number, [ + 'geospatial' + ]) + expect(linked).toEqual({ + linkedMetadataBlocks: { + before: [], + after: ['geospatial'] + } + }) + }) + }) + + describe('setAvailableLicensesForDatasetType', () => { + test('should allow for setting available licenses for a dataset type', async () => { + const randomName = `datasetType-${crypto.randomUUID().slice(0, 6)}` + const actual: DatasetType = await addDatasetType.execute({ + name: randomName, + linkedMetadataBlocks: [], + availableLicenses: [] + }) + expect(actual.name).toEqual(randomName) + + const linked: void = await setAvailableLicensesForDatasetType.execute(actual.id as number, [ + 'CC BY 4.0' + ]) + expect(linked).toEqual({ + availableLicenses: { + before: [], + after: ['CC BY 4.0'] + } + }) + }) + }) }) diff --git a/test/unit/datasets/DeleteDatasetType.test.ts b/test/unit/datasets/DeleteDatasetType.test.ts new file mode 100644 index 00000000..b8104a7a --- /dev/null +++ b/test/unit/datasets/DeleteDatasetType.test.ts @@ -0,0 +1,23 @@ +import { DeleteDatasetType } from '../../../src/datasets/domain/useCases/DeleteDatasetType' +import { IDatasetsRepository } from '../../../src/datasets/domain/repositories/IDatasetsRepository' +import { WriteError } from '../../../src' + +describe('execute', () => { + test('should return undefined on delete success', async () => { + const datasetsRepositoryStub: IDatasetsRepository = {} as IDatasetsRepository + datasetsRepositoryStub.deleteDatasetType = jest.fn().mockResolvedValue(undefined) + const sut = new DeleteDatasetType(datasetsRepositoryStub) + + const actual = await sut.execute(1) + expect(actual).toEqual(undefined) + }) + + test('should return error result on delete error', async () => { + const datasetsRepositoryStub: IDatasetsRepository = {} as IDatasetsRepository + datasetsRepositoryStub.deleteDatasetType = jest.fn().mockRejectedValue(new WriteError()) + const sut = new DeleteDatasetType(datasetsRepositoryStub) + + const nonExistentDatasetTypeId = 111 + await expect(sut.execute(nonExistentDatasetTypeId)).rejects.toThrow(WriteError) + }) +}) diff --git a/test/unit/datasets/GetDatasetAvailableDatasetType.test.ts b/test/unit/datasets/GetDatasetAvailableDatasetType.test.ts new file mode 100644 index 00000000..d6b4d958 --- /dev/null +++ b/test/unit/datasets/GetDatasetAvailableDatasetType.test.ts @@ -0,0 +1,54 @@ +import { GetDatasetAvailableDatasetType } from '../../../src/datasets/domain/useCases/GetDatasetAvailableDatasetType' +import { IDatasetsRepository } from '../../../src/datasets/domain/repositories/IDatasetsRepository' +import { DatasetType } from '../../../src/datasets/domain/models/DatasetType' +import { ReadError } from '../../../src' + +describe('GetDatasetAvailableDatasetType', () => { + const datasetTypesRepositoryStub: IDatasetsRepository = {} as IDatasetsRepository + + const datasetTypeId = 1 + const datasetTypeName = 'dataset' + const expectedDatasetType: DatasetType = { + id: datasetTypeId, + name: datasetTypeName, + linkedMetadataBlocks: [], + availableLicenses: [] + } + + it('should get a dataset type by database id', async () => { + datasetTypesRepositoryStub.getDatasetAvailableDatasetType = jest + .fn() + .mockResolvedValue(expectedDatasetType) + const sut = new GetDatasetAvailableDatasetType(datasetTypesRepositoryStub) + + const actual = await sut.execute(datasetTypeId) + + expect(actual).toEqual(expectedDatasetType) + expect(datasetTypesRepositoryStub.getDatasetAvailableDatasetType).toHaveBeenCalledTimes(1) + }) + + it('should get a dataset type by name', async () => { + datasetTypesRepositoryStub.getDatasetAvailableDatasetType = jest + .fn() + .mockResolvedValue(expectedDatasetType) + const sut = new GetDatasetAvailableDatasetType(datasetTypesRepositoryStub) + + const actual = await sut.execute(datasetTypeName) + + expect(actual).toEqual(expectedDatasetType) + expect(datasetTypesRepositoryStub.getDatasetAvailableDatasetType).toHaveBeenCalledTimes(1) + }) + + test('should return error result on repository error', async () => { + const datasetsRepositoryStub: IDatasetsRepository = {} as IDatasetsRepository + const datasetTypeId = 1 + const expectedError = new ReadError('Failed to fetch dataset type') + datasetsRepositoryStub.getDatasetAvailableDatasetType = jest + .fn() + .mockRejectedValue(expectedError) + const sut = new GetDatasetAvailableDatasetType(datasetsRepositoryStub) + + await expect(sut.execute(datasetTypeId)).rejects.toThrow(ReadError) + expect(datasetsRepositoryStub.getDatasetAvailableDatasetType).toHaveBeenCalledTimes(1) + }) +}) diff --git a/test/unit/datasets/LinkDatasetTypeWithMetadataBlocks.test.ts b/test/unit/datasets/LinkDatasetTypeWithMetadataBlocks.test.ts new file mode 100644 index 00000000..c284e0c1 --- /dev/null +++ b/test/unit/datasets/LinkDatasetTypeWithMetadataBlocks.test.ts @@ -0,0 +1,27 @@ +import { LinkDatasetTypeWithMetadataBlocks } from '../../../src/datasets/domain/useCases/LinkDatasetTypeWithMetadataBlocks' +import { IDatasetsRepository } from '../../../src/datasets/domain/repositories/IDatasetsRepository' +import { WriteError } from '../../../src' + +describe('execute', () => { + test('should return undefined on link dataset type with metadata block success', async () => { + const datasetsRepositoryStub: IDatasetsRepository = {} as IDatasetsRepository + datasetsRepositoryStub.linkDatasetTypeWithMetadataBlocks = jest + .fn() + .mockResolvedValue(undefined) + const sut = new LinkDatasetTypeWithMetadataBlocks(datasetsRepositoryStub) + + const actual = await sut.execute(1, ['geospatial']) + expect(actual).toEqual(undefined) + }) + + test('should return error result on link dataset type with metadata block error', async () => { + const datasetsRepositoryStub: IDatasetsRepository = {} as IDatasetsRepository + datasetsRepositoryStub.linkDatasetTypeWithMetadataBlocks = jest + .fn() + .mockRejectedValue(new WriteError()) + const sut = new LinkDatasetTypeWithMetadataBlocks(datasetsRepositoryStub) + + const nonExistentDatasetTypeId = 111 + await expect(sut.execute(nonExistentDatasetTypeId, ['geospatial'])).rejects.toThrow(WriteError) + }) +}) diff --git a/test/unit/datasets/SetAvailableLicensesForDatasetType.test.ts b/test/unit/datasets/SetAvailableLicensesForDatasetType.test.ts new file mode 100644 index 00000000..965ebcef --- /dev/null +++ b/test/unit/datasets/SetAvailableLicensesForDatasetType.test.ts @@ -0,0 +1,27 @@ +import { SetAvailableLicensesForDatasetType } from '../../../src/datasets/domain/useCases/SetAvailableLicensesForDatasetType' +import { IDatasetsRepository } from '../../../src/datasets/domain/repositories/IDatasetsRepository' +import { WriteError } from '../../../src' + +describe('execute', () => { + test('should return undefined on set available licenses for dataset type success', async () => { + const datasetsRepositoryStub: IDatasetsRepository = {} as IDatasetsRepository + datasetsRepositoryStub.setAvailableLicensesForDatasetType = jest + .fn() + .mockResolvedValue(undefined) + const sut = new SetAvailableLicensesForDatasetType(datasetsRepositoryStub) + + const actual = await sut.execute(1, ['geospatial']) + expect(actual).toEqual(undefined) + }) + + test('should return error result on set available licenses for dataset type error', async () => { + const datasetsRepositoryStub: IDatasetsRepository = {} as IDatasetsRepository + datasetsRepositoryStub.setAvailableLicensesForDatasetType = jest + .fn() + .mockRejectedValue(new WriteError()) + const sut = new SetAvailableLicensesForDatasetType(datasetsRepositoryStub) + + const nonExistentDatasetTypeId = 111 + await expect(sut.execute(nonExistentDatasetTypeId, ['geospatial'])).rejects.toThrow(WriteError) + }) +}) From 6f5df7ba3071adfe5a5324e89675fd5fab77181c Mon Sep 17 00:00:00 2001 From: Philip Durbin Date: Tue, 23 Sep 2025 09:03:27 -0400 Subject: [PATCH 2/3] delete dataset types after testing is done #370 --- .../datasets/LinkDatasetTypeWithMetadataBlocks.test.ts | 4 ++++ .../datasets/SetAvailableLicensesForDatasetType.test.ts | 4 ++++ 2 files changed, 8 insertions(+) diff --git a/test/functional/datasets/LinkDatasetTypeWithMetadataBlocks.test.ts b/test/functional/datasets/LinkDatasetTypeWithMetadataBlocks.test.ts index ee10efc1..c6f4c3f8 100644 --- a/test/functional/datasets/LinkDatasetTypeWithMetadataBlocks.test.ts +++ b/test/functional/datasets/LinkDatasetTypeWithMetadataBlocks.test.ts @@ -2,6 +2,7 @@ import { ApiConfig, DatasetType, addDatasetType, + deleteDatasetType, linkDatasetTypeWithMetadataBlocks } from '../../../src' import { DataverseApiAuthMechanism } from '../../../src/core/infra/repositories/ApiConfig' @@ -35,6 +36,9 @@ describe('LinkDatasetTypeWithMetadataBlocks', () => { after: ['geospatial'] } }) + + const deleted: void = await deleteDatasetType.execute(actual.id as number) + expect(deleted).toEqual({ message: 'deleted' }) }) }) }) diff --git a/test/functional/datasets/SetAvailableLicensesForDatasetType.test.ts b/test/functional/datasets/SetAvailableLicensesForDatasetType.test.ts index fdad0d2c..0c4d6876 100644 --- a/test/functional/datasets/SetAvailableLicensesForDatasetType.test.ts +++ b/test/functional/datasets/SetAvailableLicensesForDatasetType.test.ts @@ -2,6 +2,7 @@ import { ApiConfig, DatasetType, addDatasetType, + deleteDatasetType, setAvailableLicensesForDatasetType } from '../../../src' import { DataverseApiAuthMechanism } from '../../../src/core/infra/repositories/ApiConfig' @@ -35,6 +36,9 @@ describe('SetAvailableLicensesForDatasetType', () => { after: ['CC BY 4.0'] } }) + + const deleted: void = await deleteDatasetType.execute(actual.id as number) + expect(deleted).toEqual({ message: 'deleted' }) }) }) }) From fd1779c504cd266218af422ccad7bd2f54b30c7f Mon Sep 17 00:00:00 2001 From: Philip Durbin Date: Tue, 23 Sep 2025 09:16:37 -0400 Subject: [PATCH 3/3] make prettier happy #370 --- docs/useCases.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/useCases.md b/docs/useCases.md index ece5c722..ff537daa 100644 --- a/docs/useCases.md +++ b/docs/useCases.md @@ -1208,7 +1208,7 @@ import { linkDatasetTypeWithMetadataBlocks } from '@iqss/dataverse-client-javasc /* ... */ -linkDatasetTypeWithMetadataBlocks.execute(datasetTypeId, ["geospatial"]).then(() => { +linkDatasetTypeWithMetadataBlocks.execute(datasetTypeId, ['geospatial']).then(() => { /* ... */ }) ``` @@ -1226,7 +1226,7 @@ import { setAvailableLicensesForDatasetType } from '@iqss/dataverse-client-javas /* ... */ -setAvailableLicensesForDatasetType.execute(datasetTypeId, ["CC BY 4.0"]).then(() => { +setAvailableLicensesForDatasetType.execute(datasetTypeId, ['CC BY 4.0']).then(() => { /* ... */ }) ```