Skip to content

Commit c4971c2

Browse files
authored
Merge pull request #137 from IQSS/feature/133-add-publish-dataset-use-case
133 - Add publish dataset use case
2 parents e0438a2 + d57ce4a commit c4971c2

File tree

13 files changed

+380
-17
lines changed

13 files changed

+380
-17
lines changed

docs/useCases.md

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ The different use cases currently available in the package are classified below,
2323
- [List All Datasets](#list-all-datasets)
2424
- [Datasets write use cases](#datasets-write-use-cases)
2525
- [Create a Dataset](#create-a-dataset)
26+
- [Publish a Dataset](#publish-a-dataset)
2627
- [Files](#Files)
2728
- [Files read use cases](#files-read-use-cases)
2829
- [Get a File](#get-a-file)
@@ -356,6 +357,40 @@ The above example creates the new dataset in the `root` collection since no coll
356357

357358
The use case returns a [CreatedDatasetIdentifiers](../src/datasets/domain/models/CreatedDatasetIdentifiers.ts) object, which includes the persistent and numeric identifiers of the created dataset.
358359

360+
#### Publish a Dataset
361+
362+
Publishes a Dataset, given its identifier and the type of version update to perform.
363+
364+
##### Example call:
365+
366+
```typescript
367+
import { publishDataset } from '@iqss/dataverse-client-javascript'
368+
369+
/* ... */
370+
371+
const datasetId = 'doi:10.77777/FK2/AAAAAA'
372+
const versionUpdateType = VersionUpdateType.MINOR
373+
374+
publishDataset.execute(datasetId, versionUpdateType).then((publishedDataset: Dataset) => {
375+
/* ... */
376+
})
377+
378+
/* ... */
379+
```
380+
381+
_See [use case](../src/datasets/domain/useCases/PublishDataset.ts) implementation_.
382+
383+
The above example publishes the dataset with the specified identifier and performs a minor version update. If the response
384+
is successful, the use case does not return the dataset object, but the HTTP status code `200`. Otherwise, it throws an error.
385+
If you want to perform a major version update, you must set the `versionUpdateType` parameter to `VersionUpdateType.MAJOR`.
386+
387+
The `datasetId` parameter can be a string, for persistent identifiers, or a number, for numeric identifiers.
388+
389+
The `versionUpdateType` parameter can be a [VersionUpdateType](../src/datasets/domain/models/VersionUpdateType.ts) enum value, which can be one of the following:
390+
391+
- `VersionUpdateType.MINOR`
392+
- `VersionUpdateType.MAJOR`
393+
359394
## Files
360395

361396
### Files read use cases

src/datasets/domain/models/Dataset.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -183,3 +183,8 @@ interface Software extends DatasetMetadataSubField {
183183
softwareName?: string
184184
softwareVersion?: string
185185
}
186+
187+
export enum VersionUpdateType {
188+
MAJOR = 'major',
189+
MINOR = 'minor'
190+
}

src/datasets/domain/repositories/IDatasetsRepository.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { Dataset } from '../models/Dataset'
1+
import { Dataset, VersionUpdateType } from '../models/Dataset'
22
import { DatasetLock } from '../models/DatasetLock'
33
import { DatasetPreviewSubset } from '../models/DatasetPreviewSubset'
44
import { DatasetUserPermissions } from '../models/DatasetUserPermissions'
@@ -32,4 +32,5 @@ export interface IDatasetsRepository {
3232
datasetMetadataBlocks: MetadataBlock[],
3333
collectionId: string
3434
): Promise<CreatedDatasetIdentifiers>
35+
publishDataset(datasetId: number | string, versionUpdateType: VersionUpdateType): Promise<void>
3536
}
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
import { UseCase } from '../../../core/domain/useCases/UseCase'
2+
import { VersionUpdateType } from '../models/Dataset'
3+
import { IDatasetsRepository } from '../repositories/IDatasetsRepository' // Assuming Axios for HTTP requests
4+
5+
export class PublishDataset implements UseCase<void> {
6+
private datasetsRepository: IDatasetsRepository
7+
8+
constructor(datasetsRepository: IDatasetsRepository) {
9+
this.datasetsRepository = datasetsRepository
10+
}
11+
12+
/**
13+
* Publishes a dataset, given its identifier and the type of version update type.
14+
*
15+
* @param {number | string} [datasetId] - The dataset identifier, which can be a string (for persistent identifiers), or a number (for numeric identifiers).
16+
* @param {VersionUpdateType} versionUpdateType - Specifies the type of version update, 'major' or 'minor'.
17+
* @returns {Promise<void>} - This method does not return anything upon successful completion.
18+
*/
19+
async execute(datasetId: number | string, versionUpdateType: VersionUpdateType): Promise<void> {
20+
return await this.datasetsRepository.publishDataset(datasetId, versionUpdateType)
21+
}
22+
}

src/datasets/index.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import { GetDatasetSummaryFieldNames } from './domain/useCases/GetDatasetSummary
1313
import { GetPrivateUrlDatasetCitation } from './domain/useCases/GetPrivateUrlDatasetCitation'
1414
import { SingleMetadataFieldValidator } from './domain/useCases/validators/SingleMetadataFieldValidator'
1515
import { MultipleMetadataFieldValidator } from './domain/useCases/validators/MultipleMetadataFieldValidator'
16+
import { PublishDataset } from './domain/useCases/PublishDataset'
1617

1718
const datasetsRepository = new DatasetsRepository()
1819

@@ -34,6 +35,7 @@ const createDataset = new CreateDataset(
3435
new MetadataBlocksRepository(),
3536
new NewDatasetResourceValidator(metadataFieldValidator)
3637
)
38+
const publishDataset = new PublishDataset(datasetsRepository)
3739

3840
export {
3941
getDataset,
@@ -44,7 +46,8 @@ export {
4446
getDatasetUserPermissions,
4547
getDatasetSummaryFieldNames,
4648
getPrivateUrlDatasetCitation,
47-
createDataset
49+
createDataset,
50+
publishDataset
4851
}
4952
export { DatasetNotNumberedVersion } from './domain/models/DatasetNotNumberedVersion'
5053
export { DatasetUserPermissions } from './domain/models/DatasetUserPermissions'
@@ -70,3 +73,4 @@ export {
7073
NewDatasetMetadataChildFieldValueDTO as NewDatasetMetadataChildFieldValue
7174
} from './domain/dtos/NewDatasetDTO'
7275
export { CreatedDatasetIdentifiers } from './domain/models/CreatedDatasetIdentifiers'
76+
export { VersionUpdateType } from './domain/models/Dataset'

src/datasets/infra/repositories/DatasetsRepository.ts

Lines changed: 21 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,17 @@
11
import { ApiRepository } from '../../../core/infra/repositories/ApiRepository'
22
import { IDatasetsRepository } from '../../domain/repositories/IDatasetsRepository'
3-
import { Dataset } from '../../domain/models/Dataset'
4-
import { DatasetLock } from '../../domain/models/DatasetLock'
3+
import { Dataset, VersionUpdateType } from '../../domain/models/Dataset'
4+
import { transformVersionResponseToDataset } from './transformers/datasetTransformers'
55
import { DatasetUserPermissions } from '../../domain/models/DatasetUserPermissions'
6+
import { transformDatasetUserPermissionsResponseToDatasetUserPermissions } from './transformers/datasetUserPermissionsTransformers'
7+
import { DatasetLock } from '../../domain/models/DatasetLock'
68
import { CreatedDatasetIdentifiers } from '../../domain/models/CreatedDatasetIdentifiers'
79
import { DatasetPreviewSubset } from '../../domain/models/DatasetPreviewSubset'
810
import { NewDatasetDTO } from '../../domain/dtos/NewDatasetDTO'
911
import { MetadataBlock } from '../../../metadataBlocks'
10-
import { transformVersionResponseToDataset } from './transformers/datasetTransformers'
1112
import { transformNewDatasetModelToRequestPayload } from './transformers/newDatasetTransformers'
1213
import { transformDatasetLocksResponseToDatasetLocks } from './transformers/datasetLocksTransformers'
1314
import { transformDatasetPreviewsResponseToDatasetPreviewSubset } from './transformers/datasetPreviewsTransformers'
14-
import { transformDatasetUserPermissionsResponseToDatasetUserPermissions } from './transformers/datasetUserPermissionsTransformers'
1515

1616
export interface GetAllDatasetPreviewsQueryParams {
1717
per_page?: number
@@ -157,4 +157,21 @@ export class DatasetsRepository extends ApiRepository implements IDatasetsReposi
157157
throw error
158158
})
159159
}
160+
161+
public async publishDataset(
162+
datasetId: string | number,
163+
versionUpdateType: VersionUpdateType
164+
): Promise<void> {
165+
return this.doPost(
166+
this.buildApiEndpoint(this.datasetsResourceName, `actions/:publish`, datasetId),
167+
{},
168+
{
169+
type: versionUpdateType
170+
}
171+
)
172+
.then(() => undefined)
173+
.catch((error) => {
174+
throw error
175+
})
176+
}
160177
}

test/environment/setup.ts

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,11 @@ import { TestConstants } from '../testHelpers/TestConstants'
55
import datasetJson1 from '../testHelpers/datasets/test-dataset-1.json'
66
import datasetJson2 from '../testHelpers/datasets/test-dataset-2.json'
77
import datasetJson3 from '../testHelpers/datasets/test-dataset-3.json'
8+
import datasetJson4 from '../testHelpers/datasets/test-dataset-4.json'
89
import collectionJson from '../testHelpers/collections/test-collection-1.json'
910
import { ROOT_COLLECTION_ALIAS } from '../../src/collections/domain/models/Collection'
1011

12+
const NUMBER_OF_DATASETS = 4
1113
const COMPOSE_FILE = 'docker-compose.yml'
1214

1315
const CONTAINER_DATAVERSE_BOOTSTRAP_NAME = 'test_dataverse_bootstrap'
@@ -63,6 +65,9 @@ async function setupTestFixtures(): Promise<void> {
6365
await createDatasetViaApi(datasetJson2).catch(() => {
6466
console.error('Tests setup: Error while creating test Dataset 2')
6567
})
68+
await createDatasetViaApi(datasetJson4).catch(() => {
69+
console.error('Tests setup: Error while creating test Dataset 4')
70+
})
6671
await createCollectionViaApi(collectionJson)
6772
.then()
6873
.catch(() => {
@@ -108,7 +113,7 @@ async function waitForDatasetsIndexingInSolr(): Promise<void> {
108113
.get(`${TestConstants.TEST_API_URL}/search?q=*&type=dataset`, buildRequestHeaders())
109114
.then((response) => {
110115
const nDatasets = response.data.data.items.length
111-
if (nDatasets === 3) {
116+
if (nDatasets === NUMBER_OF_DATASETS) {
112117
datasetsIndexed = true
113118
}
114119
})
Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
import {
2+
ApiConfig,
3+
createDataset,
4+
publishDataset,
5+
VersionUpdateType,
6+
WriteError
7+
} from '../../../src'
8+
import { TestConstants } from '../../testHelpers/TestConstants'
9+
import { DataverseApiAuthMechanism } from '../../../src/core/infra/repositories/ApiConfig'
10+
import { waitForNoLocks } from '../../testHelpers/datasets/datasetHelper'
11+
12+
const testNewDataset = {
13+
license: {
14+
name: 'CC0 1.0',
15+
uri: 'http://creativecommons.org/publicdomain/zero/1.0',
16+
iconUri: 'https://licensebuttons.net/p/zero/1.0/88x31.png'
17+
},
18+
metadataBlockValues: [
19+
{
20+
name: 'citation',
21+
fields: {
22+
title: 'Dataset created using the createDataset use case',
23+
author: [
24+
{
25+
authorName: 'Admin, Dataverse',
26+
authorAffiliation: 'Dataverse.org'
27+
},
28+
{
29+
authorName: 'Owner, Dataverse',
30+
authorAffiliation: 'Dataversedemo.org'
31+
}
32+
],
33+
datasetContact: [
34+
{
35+
datasetContactEmail: '[email protected]',
36+
datasetContactName: 'Finch, Fiona'
37+
}
38+
],
39+
dsDescription: [
40+
{
41+
dsDescriptionValue: 'This is the description of the dataset.'
42+
}
43+
],
44+
subject: ['Medicine, Health and Life Sciences']
45+
}
46+
}
47+
]
48+
}
49+
50+
describe('execute', () => {
51+
beforeEach(async () => {
52+
ApiConfig.init(
53+
TestConstants.TEST_API_URL,
54+
DataverseApiAuthMechanism.API_KEY,
55+
process.env.TEST_API_KEY
56+
)
57+
})
58+
59+
test('should successfully publish a dataset', async () => {
60+
const dataset = await createDataset.execute(testNewDataset)
61+
62+
const response = await publishDataset.execute(dataset.persistentId, VersionUpdateType.MAJOR)
63+
await waitForNoLocks(dataset.numericId, 10)
64+
65+
expect(response).toBeUndefined()
66+
})
67+
68+
test('should throw an error when trying to publish a dataset that does not exist', async () => {
69+
const nonExistentTestDatasetId = 'non-existent-dataset'
70+
const expectedError = new WriteError(
71+
`[404] Dataset with Persistent ID ${nonExistentTestDatasetId} not found.`
72+
)
73+
74+
await expect(
75+
publishDataset.execute(nonExistentTestDatasetId, VersionUpdateType.MAJOR)
76+
).rejects.toThrow(expectedError)
77+
})
78+
})

test/integration/datasets/DatasetsRepository.test.ts

Lines changed: 38 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2,17 +2,18 @@ import { DatasetsRepository } from '../../../src/datasets/infra/repositories/Dat
22
import { TestConstants } from '../../testHelpers/TestConstants'
33
import {
44
createPrivateUrlViaApi,
5-
publishDatasetViaApi,
65
deaccessionDatasetViaApi,
6+
publishDatasetViaApi,
77
waitForNoLocks
88
} from '../../testHelpers/datasets/datasetHelper'
99
import { ReadError } from '../../../src/core/domain/repositories/ReadError'
1010
import {
11-
DatasetNotNumberedVersion,
1211
DatasetLockType,
13-
DatasetPreviewSubset
12+
DatasetNotNumberedVersion,
13+
DatasetPreviewSubset,
14+
VersionUpdateType
1415
} from '../../../src/datasets'
15-
import { ApiConfig } from '../../../src'
16+
import { ApiConfig, WriteError } from '../../../src'
1617
import { DataverseApiAuthMechanism } from '../../../src/core/infra/repositories/ApiConfig'
1718
import { MetadataBlocksRepository } from '../../../src/metadataBlocks/infra/repositories/MetadataBlocksRepository'
1819
import {
@@ -46,7 +47,7 @@ describe('DatasetsRepository', () => {
4647

4748
describe('getAllDatasetPreviews', () => {
4849
const testPageLimit = 1
49-
const expectedTotalDatasetCount = 3
50+
const expectedTotalDatasetCount = 4
5051

5152
test('should return all dataset previews when no pagination params are defined', async () => {
5253
const actual: DatasetPreviewSubset = await sut.getAllDatasetPreviews()
@@ -65,20 +66,21 @@ describe('DatasetsRepository', () => {
6566
test('should return second dataset preview page', async () => {
6667
const actual = await sut.getAllDatasetPreviews(testPageLimit, 1)
6768
expect(actual.datasetPreviews.length).toEqual(1)
68-
expect(actual.datasetPreviews[0].title).toMatch('Second Dataset')
69+
expect(actual.datasetPreviews[0].title).toMatch('Fourth Dataset')
6970
expect(actual.totalDatasetCount).toEqual(expectedTotalDatasetCount)
7071
})
7172

7273
test('should return third dataset preview page', async () => {
7374
const actual = await sut.getAllDatasetPreviews(testPageLimit, 2)
7475
expect(actual.datasetPreviews.length).toEqual(1)
75-
expect(actual.datasetPreviews[0].title).toMatch('First Dataset')
76+
expect(actual.datasetPreviews[0].title).toMatch('Second Dataset')
7677
expect(actual.totalDatasetCount).toEqual(expectedTotalDatasetCount)
7778
})
7879

7980
test('should return fourth dataset preview page', async () => {
8081
const actual = await sut.getAllDatasetPreviews(testPageLimit, 3)
81-
expect(actual.datasetPreviews.length).toEqual(0)
82+
expect(actual.datasetPreviews.length).toEqual(1)
83+
expect(actual.datasetPreviews[0].title).toMatch('First Dataset')
8284
expect(actual.totalDatasetCount).toEqual(expectedTotalDatasetCount)
8385
})
8486

@@ -377,4 +379,32 @@ describe('DatasetsRepository', () => {
377379
)
378380
})
379381
})
382+
383+
describe('publishDataset', () => {
384+
test('should publish a new dataset version', async () => {
385+
const expectedMajorVersion = 1
386+
await waitForNoLocks(TestConstants.TEST_CREATED_DATASET_4_ID, 10)
387+
388+
await sut.publishDataset(TestConstants.TEST_CREATED_DATASET_4_ID, VersionUpdateType.MAJOR)
389+
await waitForNoLocks(TestConstants.TEST_CREATED_DATASET_4_ID, 10)
390+
391+
const newDatasetVersion = await sut.getDataset(
392+
TestConstants.TEST_CREATED_DATASET_4_ID,
393+
latestVersionId,
394+
false
395+
)
396+
397+
expect(newDatasetVersion.versionInfo.majorNumber).toBe(expectedMajorVersion)
398+
})
399+
400+
test('should return error when dataset does not exist', async () => {
401+
const expectedError = new WriteError(
402+
`[404] Dataset with ID ${nonExistentTestDatasetId} not found.`
403+
)
404+
405+
await expect(
406+
sut.publishDataset(nonExistentTestDatasetId, VersionUpdateType.MAJOR)
407+
).rejects.toThrow(expectedError)
408+
})
409+
})
380410
})

test/testHelpers/TestConstants.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -47,10 +47,11 @@ export class TestConstants {
4747
}
4848
static readonly TEST_CREATED_DATASET_1_ID = 2
4949
static readonly TEST_CREATED_DATASET_2_ID = 3
50-
static readonly TEST_CREATED_DATASET_3_ID = 4
50+
static readonly TEST_CREATED_DATASET_3_ID = 5
51+
static readonly TEST_CREATED_DATASET_4_ID = 4
5152
static readonly TEST_DUMMY_COLLECTION_ID = 10001
5253
static readonly TEST_DUMMY_COLLECTION_ALIAS = 'dummyCollectionId'
53-
static readonly TEST_CREATED_COLLECTION_1_ID = 4
54+
static readonly TEST_CREATED_COLLECTION_1_ID = 5
5455
static readonly TEST_CREATED_COLLECTION_1_ALIAS = 'firstCollection'
5556
static readonly TEST_CREATED_COLLECTION_1_ROOT = ROOT_COLLECTION_ALIAS
5657
}

0 commit comments

Comments
 (0)