Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,10 @@ This changelog follows the principles of [Keep a Changelog](https://keepachangel

### Added

- Datasets: Added `returnDatasetToAuthor` to return drafts in review with a reason, supporting numeric and persistent identifiers and configured authentication.

- Datasets: Added public use cases for allowed curation labels, current status, status history, setting/removing labels, and submitting drafts for review, with numeric and persistent identifier support.

### Changed

### Fixed
Expand Down
131 changes: 131 additions & 0 deletions docs/useCases.md
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,9 @@ The different use cases currently available in the package are classified below,
- [Unset Template As Default](#unset-template-as-default)
- [Datasets](#Datasets)
- [Datasets read use cases](#datasets-read-use-cases)
- [Get Allowed Dataset Curation Labels](#get-allowed-dataset-curation-labels)
- [Get Dataset Curation Status](#get-dataset-curation-status)
- [Get Dataset Curation Status History](#get-dataset-curation-status-history)
- [Get a Dataset](#get-a-dataset)
- [Get Dataset By Private URL Token](#get-dataset-by-private-url-token)
- [Get Dataset Citation Text](#get-dataset-citation-text)
Expand All @@ -65,6 +68,10 @@ The different use cases currently available in the package are classified below,
- [Get Dataset Available Dataset Type](#get-dataset-available-dataset-type)
- [Get Dataset Upload Limits](#get-dataset-upload-limits)
- [Datasets write use cases](#datasets-write-use-cases)
- [Set Dataset Curation Status](#set-dataset-curation-status)
- [Delete Dataset Curation Status](#delete-dataset-curation-status)
- [Submit Dataset For Review](#submit-dataset-for-review)
- [Return Dataset To Author](#return-dataset-to-author)
- [Create a Dataset](#create-a-dataset)
- [Update a Dataset](#update-a-dataset)
- [Update a Dataset License](#update-a-dataset-license)
Expand Down Expand Up @@ -967,6 +974,65 @@ _See [use case](../src/templates/domain/useCases/UpdateTemplateTermsOfAccess.ts)

### Datasets Read Use Cases

Curation labels require the server setting `:AllowedCurationLabels`, a JSON object mapping label-set names to arrays of labels, for example `{"defaultSet": ["Privacy Review", "Final Approval"]}`. A superuser selects a label set on a collection or dataset using the administration APIs. An explicit dataset selection overrides its collection. `DEFAULT` (or no selection) inherits from the parent collection up to the root; if no set is selected there, labels are disabled. `DISABLED` explicitly disables labels. Defining the setting alone does not select a set. Labels currently allow letters, digits, underscores, and spaces. These administration operations are not exposed by these use cases.

Allowed-label reads and status mutations require publish permission. Current-status and history reads also require publish permission by default. With `dataverse.ui.show-curation-status-to-all=true`, users with permission to view the draft can read them. The backend enforces these permissions; the SDK attaches the configured API key, session cookie, or bearer token.

Labels only track curation progress: assigning, changing, or removing one does not submit for review, return to the author, publish, or change review locks. Publication clears the draft label. `DatasetCurationStatus` and `DatasetReviewSubmission` are exported from the package root.

See the [Dataverse curation API guide](https://guides.dataverse.org/en/latest/api/curation-labels.html) for server configuration and API behavior.

#### Get Allowed Dataset Curation Labels

Returns the effective configured labels in server order. Requires publish permission. Labels are returned unchanged, including whitespace.

The `datasetId` accepts a numeric ID or persistent identifier. Configure authentication with `ApiConfig` before calling the use case. Backend failures propagate as `ReadError`.

##### Example call:

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

const datasetId = 'doi:10.77777/FK2/AAAAAA'
await getAllowedDatasetCurationLabels.execute(datasetId) // Promise<string[]>
```

_See [use case](../src/datasets/domain/useCases/curation/GetAllowedDatasetCurationLabels.ts) implementation_.

#### Get Dataset Curation Status

Returns the current curation record, or `null` when no record exists. A removal is a record with no `label` (`undefined` when read); it retains its timestamp and assigner.

The `datasetId` accepts a numeric ID or persistent identifier. Configure authentication with `ApiConfig` before calling the use case. Backend failures propagate as `ReadError`.

##### Example call:

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

const datasetId = 'doi:10.77777/FK2/AAAAAA'
await getDatasetCurationStatus.execute(datasetId) // Promise<DatasetCurationStatus | null>
```

_See [use case](../src/datasets/domain/useCases/curation/GetDatasetCurationStatus.ts) implementation_.

#### Get Dataset Curation Status History

Returns all assignments and removals, newest first, or an empty array when no history exists. Missing legacy `createTime` and `assigner` fields remain `undefined`. Timestamps remain server strings without timezone conversion.

The `datasetId` accepts a numeric ID or persistent identifier. Configure authentication with `ApiConfig` before calling the use case. Backend failures propagate as `ReadError`.

##### Example call:

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

const datasetId = 'doi:10.77777/FK2/AAAAAA'
await getDatasetCurationStatusHistory.execute(datasetId) // Promise<DatasetCurationStatus[]>
```

_See [use case](../src/datasets/domain/useCases/curation/GetDatasetCurationStatusHistory.ts) implementation_.

#### Get a Dataset

Returns a [Dataset](../src/datasets/domain/models/Dataset.ts) instance, given the search parameters to identify it.
Expand Down Expand Up @@ -1423,6 +1489,71 @@ Example returned value:

### Datasets Write Use Cases

#### Set Dataset Curation Status

Assigns or changes a configured label on a draft. Requires publish permission. Pass the exact value returned by `getAllowedDatasetCurationLabels`; reserved characters are safely encoded. Unconfigured labels, unchanged labels, and datasets without a draft are rejected by the backend.

The `datasetId` accepts a numeric ID or persistent identifier. Configure authentication with `ApiConfig` before calling the use case. Backend failures propagate as `WriteError`.

##### Example call:

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

const datasetId = 'doi:10.77777/FK2/AAAAAA'
await setDatasetCurationStatus.execute(datasetId, 'Privacy Review') // Promise<void>
```

_See [use case](../src/datasets/domain/useCases/curation/SetDatasetCurationStatus.ts) implementation_.

#### Delete Dataset Curation Status

Removes the current draft label and retains a removal record in its history. Requires publish permission. This does not return the dataset to its author or remove review locks.

The `datasetId` accepts a numeric ID or persistent identifier. Configure authentication with `ApiConfig` before calling the use case. Backend failures propagate as `WriteError`.

##### Example call:

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

const datasetId = 'doi:10.77777/FK2/AAAAAA'
await deleteDatasetCurationStatus.execute(datasetId) // Promise<void>
```

_See [use case](../src/datasets/domain/useCases/curation/DeleteDatasetCurationStatus.ts) implementation_.

#### Return Dataset To Author

Returns a draft currently in review to its authors. Requires authentication and publish permission. The server removes the review lock, records the reason, and notifies authors. Backend validation and permission failures are exposed as `WriteError`.

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

await returnDatasetToAuthor.execute(datasetId, 'Please add missing metadata.') // Promise<void>
```

`datasetId` accepts a numeric ID or persistent identifier. `reasonForReturn` is required unless the server disables that requirement; the SDK sends the supplied text unchanged.

_See [use case](../src/datasets/domain/useCases/curation/ReturnDatasetToAuthor.ts) implementation and [API documentation](https://guides.dataverse.org/en/latest/api/native-api.html#return-a-dataset-to-author)._

#### Submit Dataset For Review

Submits a draft for review and returns `{ inReview: boolean, message: string }`. Requires an authenticated user with edit permission; anonymous submission is not supported by the backend. This adds a review lock and notifies curators. Drafts already in review and datasets without a draft are rejected.

The `datasetId` accepts a numeric ID or persistent identifier. Configure authentication with `ApiConfig` before calling the use case. Backend failures propagate as `WriteError`.

##### Example call:

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

const datasetId = 'doi:10.77777/FK2/AAAAAA'
await submitDatasetForReview.execute(datasetId) // Promise<DatasetReviewSubmission>
```

_See [use case](../src/datasets/domain/useCases/curation/SubmitDatasetForReview.ts) implementation_.

#### Create a Dataset

Creates a new Dataset in a collection, given a [DatasetDTO](../src/datasets/domain/dtos/DatasetDTO.ts) object, an optional collection identifier, which defaults to `:root`, and an optional dataset type.
Expand Down
10 changes: 10 additions & 0 deletions src/datasets/domain/models/DatasetCuration.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
export interface DatasetCurationStatus {
label?: string
createTime?: string
assigner?: string
}

export interface DatasetReviewSubmission {
inReview: boolean
message: string
}
9 changes: 9 additions & 0 deletions src/datasets/domain/repositories/IDatasetsRepository.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,17 @@ import { DatasetUploadLimits } from '../models/DatasetUploadLimits'
import { DatasetReview } from '../models/DatasetReview'
import { ExportedDatasetMetadata } from '../models/ExportedDatasetMetadata'
import { DatasetNotNumberedVersion } from '../models/DatasetNotNumberedVersion'
import { DatasetCurationStatus, DatasetReviewSubmission } from '../models/DatasetCuration'

export interface IDatasetsRepository {
getAllowedDatasetCurationLabels(datasetId: number | string): Promise<string[]>
getDatasetCurationStatus(datasetId: number | string): Promise<DatasetCurationStatus | null>
getDatasetCurationStatusHistory(datasetId: number | string): Promise<DatasetCurationStatus[]>
setDatasetCurationStatus(datasetId: number | string, label: string): Promise<void>
deleteDatasetCurationStatus(datasetId: number | string): Promise<void>
returnDatasetToAuthor(datasetId: number | string, reasonForReturn: string): Promise<void>
submitDatasetForReview(datasetId: number | string): Promise<DatasetReviewSubmission>

getDataset(
datasetId: number | string,
datasetVersionId: string,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import { UseCase } from '../../../../core/domain/useCases/UseCase'
import { IDatasetsRepository } from '../../repositories/IDatasetsRepository'

export class DeleteDatasetCurationStatus implements UseCase<void> {
constructor(private readonly datasetsRepository: IDatasetsRepository) {}

/**
* Removes the label from a draft, retaining history. Requires publish permission.
* @param datasetId Numeric dataset ID or persistent identifier.
*/
async execute(datasetId: number | string): Promise<void> {
return await this.datasetsRepository.deleteDatasetCurationStatus(datasetId)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import { UseCase } from '../../../../core/domain/useCases/UseCase'
import { IDatasetsRepository } from '../../repositories/IDatasetsRepository'

export class GetAllowedDatasetCurationLabels implements UseCase<string[]> {
constructor(private readonly datasetsRepository: IDatasetsRepository) {}

/**
* Returns the effective allowed labels. Requires publish permission.
* @param datasetId Numeric dataset ID or persistent identifier.
*/
async execute(datasetId: number | string): Promise<string[]> {
return await this.datasetsRepository.getAllowedDatasetCurationLabels(datasetId)
}
}
15 changes: 15 additions & 0 deletions src/datasets/domain/useCases/curation/GetDatasetCurationStatus.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import { UseCase } from '../../../../core/domain/useCases/UseCase'
import { IDatasetsRepository } from '../../repositories/IDatasetsRepository'
import { DatasetCurationStatus } from '../../models/DatasetCuration'

export class GetDatasetCurationStatus implements UseCase<DatasetCurationStatus | null> {
constructor(private readonly datasetsRepository: IDatasetsRepository) {}

/**
* Returns the latest curation record, or null if none exists. Visibility is enforced by the server.
* @param datasetId Numeric dataset ID or persistent identifier.
*/
async execute(datasetId: number | string): Promise<DatasetCurationStatus | null> {
return await this.datasetsRepository.getDatasetCurationStatus(datasetId)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import { UseCase } from '../../../../core/domain/useCases/UseCase'
import { IDatasetsRepository } from '../../repositories/IDatasetsRepository'
import { DatasetCurationStatus } from '../../models/DatasetCuration'

export class GetDatasetCurationStatusHistory implements UseCase<DatasetCurationStatus[]> {
constructor(private readonly datasetsRepository: IDatasetsRepository) {}

/**
* Returns curation assignments and removals in server order, newest first.
* @param datasetId Numeric dataset ID or persistent identifier.
*/
async execute(datasetId: number | string): Promise<DatasetCurationStatus[]> {
return await this.datasetsRepository.getDatasetCurationStatusHistory(datasetId)
}
}
17 changes: 17 additions & 0 deletions src/datasets/domain/useCases/curation/ReturnDatasetToAuthor.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import { UseCase } from '../../../../core/domain/useCases/UseCase'
import { IDatasetsRepository } from '../../repositories/IDatasetsRepository'

export class ReturnDatasetToAuthor implements UseCase<void> {
constructor(private readonly datasetsRepository: IDatasetsRepository) {}

/**
* Returns a draft in review to its authors. Requires publish permission.
* The server records the reason and notifies the authors.
* @param datasetId Numeric dataset ID or persistent identifier.
* @param reasonForReturn Reason sent to the authors; required unless disabled by server configuration.
* @see https://guides.dataverse.org/en/latest/api/native-api.html#return-a-dataset-to-author
*/
async execute(datasetId: number | string, reasonForReturn: string): Promise<void> {
return await this.datasetsRepository.returnDatasetToAuthor(datasetId, reasonForReturn)
}
}
14 changes: 14 additions & 0 deletions src/datasets/domain/useCases/curation/SetDatasetCurationStatus.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import { UseCase } from '../../../../core/domain/useCases/UseCase'
import { IDatasetsRepository } from '../../repositories/IDatasetsRepository'

export class SetDatasetCurationStatus implements UseCase<void> {
constructor(private readonly datasetsRepository: IDatasetsRepository) {}

/**
* Assigns a configured label to a draft. Requires publish permission; does not change review or publication state.
* @param datasetId Numeric dataset ID or persistent identifier.
*/
async execute(datasetId: number | string, label: string): Promise<void> {
return await this.datasetsRepository.setDatasetCurationStatus(datasetId, label)
}
}
15 changes: 15 additions & 0 deletions src/datasets/domain/useCases/curation/SubmitDatasetForReview.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import { UseCase } from '../../../../core/domain/useCases/UseCase'
import { IDatasetsRepository } from '../../repositories/IDatasetsRepository'
import { DatasetReviewSubmission } from '../../models/DatasetCuration'

export class SubmitDatasetForReview implements UseCase<DatasetReviewSubmission> {
constructor(private readonly datasetsRepository: IDatasetsRepository) {}

/**
* Submits a draft for review. Requires authentication and edit permission; anonymous submissions are not supported.
* @param datasetId Numeric dataset ID or persistent identifier.
*/
async execute(datasetId: number | string): Promise<DatasetReviewSubmission> {
return await this.datasetsRepository.submitDatasetForReview(datasetId)
}
}
23 changes: 23 additions & 0 deletions src/datasets/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,8 +38,22 @@ import { GetDatasetStorageDriver } from './domain/useCases/GetDatasetStorageDriv
import { GetDatasetUploadLimits } from './domain/useCases/GetDatasetUploadLimits'
import { GetDatasetReviews } from './domain/useCases/GetDatasetReviews'
import { ExportDatasetMetadata } from './domain/useCases/ExportDatasetMetadata'
import { GetAllowedDatasetCurationLabels } from './domain/useCases/curation/GetAllowedDatasetCurationLabels'
import { GetDatasetCurationStatus } from './domain/useCases/curation/GetDatasetCurationStatus'
import { GetDatasetCurationStatusHistory } from './domain/useCases/curation/GetDatasetCurationStatusHistory'
import { SetDatasetCurationStatus } from './domain/useCases/curation/SetDatasetCurationStatus'
import { DeleteDatasetCurationStatus } from './domain/useCases/curation/DeleteDatasetCurationStatus'
import { ReturnDatasetToAuthor } from './domain/useCases/curation/ReturnDatasetToAuthor'
import { SubmitDatasetForReview } from './domain/useCases/curation/SubmitDatasetForReview'

const datasetsRepository = new DatasetsRepository()
const getAllowedDatasetCurationLabels = new GetAllowedDatasetCurationLabels(datasetsRepository)
const getDatasetCurationStatus = new GetDatasetCurationStatus(datasetsRepository)
const getDatasetCurationStatusHistory = new GetDatasetCurationStatusHistory(datasetsRepository)
const setDatasetCurationStatus = new SetDatasetCurationStatus(datasetsRepository)
const deleteDatasetCurationStatus = new DeleteDatasetCurationStatus(datasetsRepository)
const returnDatasetToAuthor = new ReturnDatasetToAuthor(datasetsRepository)
const submitDatasetForReview = new SubmitDatasetForReview(datasetsRepository)

const getDataset = new GetDataset(datasetsRepository)
const getDatasetLocks = new GetDatasetLocks(datasetsRepository)
Expand Down Expand Up @@ -93,7 +107,16 @@ const getDatasetUploadLimits = new GetDatasetUploadLimits(datasetsRepository)
const getDatasetReviews = new GetDatasetReviews(datasetsRepository)
const exportDatasetMetadata = new ExportDatasetMetadata(datasetsRepository)

export { DatasetCurationStatus, DatasetReviewSubmission } from './domain/models/DatasetCuration'

export {
getAllowedDatasetCurationLabels,
getDatasetCurationStatus,
getDatasetCurationStatusHistory,
setDatasetCurationStatus,
deleteDatasetCurationStatus,
returnDatasetToAuthor,
submitDatasetForReview,
getDataset,
getDatasetLocks,
getDatasetCitation,
Expand Down
Loading
Loading