Skip to content
Merged
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ This changelog follows the principles of [Keep a Changelog](https://keepachangel
- New Use Case: [Get Collections For Linking Use Case](./docs/useCases.md#get-collections-for-linking).
- New Use Case: [Create a Dataset Template](./docs/useCases.md#create-a-dataset-template) under Collections.

- New Use Case: [Update Terms of Access](./docs/useCases.md#update-terms-of-access).

### Changed

- Add pagination query parameters to Dataset Version Summeries and File Version Summaries use cases
Expand Down
32 changes: 32 additions & 0 deletions docs/useCases.md
Original file line number Diff line number Diff line change
Expand Up @@ -1152,6 +1152,38 @@ The `versionUpdateType` parameter can be a [VersionUpdateType](../src/datasets/d
- `VersionUpdateType.MAJOR`
- `VersionUpdateType.UPDATE_CURRENT`

#### Update Terms of Access

Updates the Terms of Access for restricted files on a dataset.

##### Example call:

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

/* ... */

const datasetId = 3

await updateTermsOfAccess.execute(datasetId, {
fileAccessRequest: true,
termsOfAccessForRestrictedFiles: 'Your terms of access for restricted files',
dataAccessPlace: 'Your data access place',
originalArchive: 'Your original archive',
availabilityStatus: 'Your availability status',
contactForAccess: 'Your contact for access',
sizeOfCollection: 'Your size of collection',
studyCompletion: 'Your study completion'
})
```

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

Notes:

- If the dataset is already published, this action creates a DRAFT version containing the new terms.
- Unspecified fields are treated as omissions: sending only `fileAccessRequest` will update that field and leave all other terms absent (undefined). In practice, the new values you send fully replace the previous set of terms — so if you omit a field, you are effectively clearing it unless you include its original value in the new input.

#### Deaccession a Dataset

Deaccession a Dataset, given its identifier, version, and deaccessionDatasetDTO to perform.
Expand Down
1 change: 1 addition & 0 deletions src/datasets/domain/models/Dataset.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ export interface CustomTerms {
conditions?: string
disclaimer?: string
}

export interface TermsOfAccess {
fileAccessRequest: boolean
termsOfAccessForRestrictedFiles?: string
Expand Down
2 changes: 2 additions & 0 deletions src/datasets/domain/repositories/IDatasetsRepository.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import { CitationFormat } from '../models/CitationFormat'
import { FormattedCitation } from '../models/FormattedCitation'
import { DatasetTemplate } from '../models/DatasetTemplate'
import { DatasetType } from '../models/DatasetType'
import { TermsOfAccess } from '../models/Dataset'
import { DatasetLicenseUpdateRequest } from '../dtos/DatasetLicenseUpdateRequest'
import { DatasetTypeDTO } from '../dtos/DatasetTypeDTO'

Expand Down Expand Up @@ -96,6 +97,7 @@ export interface IDatasetsRepository {
licenses: string[]
): Promise<void>
deleteDatasetType(datasetTypeId: number): Promise<void>
updateTermsOfAccess(datasetId: number | string, termsOfAccess: TermsOfAccess): Promise<void>
updateDatasetLicense(
datasetId: number | string,
payload: DatasetLicenseUpdateRequest
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,10 @@ export class SetAvailableLicensesForDatasetType implements UseCase<void> {

/**
* Sets the available licenses for a given dataset type. This limits the license options when creating a dataset of this type.
*
* @param {number | string} [datasetTypeId] - The dataset type identifier, which can be a string (for persistent identifiers), or a number (for numeric identifiers).
* @param {string[]} licenses - The licenses to set for the dataset type.
* @returns {Promise<void>} - This method does not return anything upon successful completion.
*/
async execute(datasetTypeId: number | string, licenses: string[]): Promise<void> {
return await this.datasetsRepository.setAvailableLicensesForDatasetType(datasetTypeId, licenses)
Expand Down
22 changes: 22 additions & 0 deletions src/datasets/domain/useCases/UpdateTermsOfAccess.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import { UseCase } from '../../../core/domain/useCases/UseCase'
import { IDatasetsRepository } from '../repositories/IDatasetsRepository'
import { TermsOfAccess } from '../models/Dataset'

export class UpdateTermsOfAccess implements UseCase<void> {
private datasetsRepository: IDatasetsRepository

constructor(datasetsRepository: IDatasetsRepository) {
this.datasetsRepository = datasetsRepository
}

/**
* Sets the terms of access for a given dataset.
*
* @param {number | string} [datasetId] - The dataset identifier, which can be a string (for persistent identifiers), or a number (for numeric identifiers).
* @param {TermsOfAccess} termsOfAccess - The terms of access to set for the dataset.
* @returns {Promise<void>} - This method does not return anything upon successful completion.
*/
async execute(datasetId: number | string, termsOfAccess: TermsOfAccess): Promise<void> {
return await this.datasetsRepository.updateTermsOfAccess(datasetId, termsOfAccess)
}
}
3 changes: 3 additions & 0 deletions src/datasets/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ import { SetAvailableLicensesForDatasetType } from './domain/useCases/SetAvailab
import { DeleteDatasetType } from './domain/useCases/DeleteDatasetType'
import { GetDatasetCitationInOtherFormats } from './domain/useCases/GetDatasetCitationInOtherFormats'
import { GetDatasetTemplates } from './domain/useCases/GetDatasetTemplates'
import { UpdateTermsOfAccess } from './domain/useCases/UpdateTermsOfAccess'
import { UpdateDatasetLicense } from './domain/useCases/UpdateDatasetLicense'

const datasetsRepository = new DatasetsRepository()
Expand Down Expand Up @@ -81,6 +82,7 @@ const setAvailableLicensesForDatasetType = new SetAvailableLicensesForDatasetTyp
const deleteDatasetType = new DeleteDatasetType(datasetsRepository)
const getDatasetCitationInOtherFormats = new GetDatasetCitationInOtherFormats(datasetsRepository)
const getDatasetTemplates = new GetDatasetTemplates(datasetsRepository)
const updateTermsOfAccess = new UpdateTermsOfAccess(datasetsRepository)
const updateDatasetLicense = new UpdateDatasetLicense(datasetsRepository)

export {
Expand All @@ -106,6 +108,7 @@ export {
getDatasetAvailableCategories,
getDatasetCitationInOtherFormats,
getDatasetTemplates,
updateTermsOfAccess,
getDatasetAvailableDatasetTypes,
getDatasetAvailableDatasetType,
addDatasetType,
Expand Down
16 changes: 16 additions & 0 deletions src/datasets/infra/repositories/DatasetsRepository.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@ import { DatasetTemplate } from '../../domain/models/DatasetTemplate'
import { DatasetTemplatePayload } from './transformers/DatasetTemplatePayload'
import { transformDatasetTemplatePayloadToDatasetTemplate } from './transformers/datasetTemplateTransformers'
import { DatasetType } from '../../domain/models/DatasetType'
import { TermsOfAccess } from '../../domain/models/Dataset'
import { transformTermsOfAccessToUpdatePayload } from './transformers/termsOfAccessTransformers'
import { DatasetLicenseUpdateRequest } from '../../domain/dtos/DatasetLicenseUpdateRequest'
import { DatasetTypeDTO } from '../../domain/dtos/DatasetTypeDTO'

Expand Down Expand Up @@ -486,6 +488,20 @@ export class DatasetsRepository extends ApiRepository implements IDatasetsReposi
})
}

public async updateTermsOfAccess(
datasetId: number | string,
termsOfAccess: TermsOfAccess
): Promise<void> {
return this.doPut(
this.buildApiEndpoint(this.datasetsResourceName, 'access', datasetId),
transformTermsOfAccessToUpdatePayload(termsOfAccess)
)
.then(() => undefined)
.catch((error) => {
throw error
})
}

public async updateDatasetLicense(
datasetId: number | string,
payload: DatasetLicenseUpdateRequest
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import { TermsOfAccess } from '../../../domain/models/Dataset'

export const transformTermsOfAccessToUpdatePayload = (
terms: TermsOfAccess & { termsOfAccess?: string }
) => {
const {
fileAccessRequest,
dataAccessPlace,
originalArchive,
availabilityStatus,
contactForAccess,
sizeOfCollection,
studyCompletion
} = terms

const termsOfAccessForRestrictedFiles =
terms.termsOfAccess ?? terms.termsOfAccessForRestrictedFiles

return {
customTermsOfAccess: {
fileAccessRequest,
termsOfAccess: termsOfAccessForRestrictedFiles,
dataAccessPlace,
originalArchive,
availabilityStatus,
contactForAccess,
sizeOfCollection,
studyCompletion
}
}
}
51 changes: 51 additions & 0 deletions test/functional/datasets/UpdateTermsOfAccess.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import { TestConstants } from '../../testHelpers/TestConstants'
import {
ApiConfig,
DataverseApiAuthMechanism
} from '../../../src/core/infra/repositories/ApiConfig'
import {
createDataset,
DatasetNotNumberedVersion,
getDataset,
updateTermsOfAccess
} from '../../../src/datasets'
import { WriteError } from '../../../src'

describe('UpdateTermsOfAccess (functional)', () => {
beforeAll(() => {
ApiConfig.init(
TestConstants.TEST_API_URL,
DataverseApiAuthMechanism.API_KEY,
process.env.TEST_API_KEY
)
})

test('should update terms of access with provided fields', async () => {
const ids = await createDataset.execute(TestConstants.TEST_NEW_DATASET_DTO)

await updateTermsOfAccess.execute(ids.numericId, {
fileAccessRequest: true,
termsOfAccessForRestrictedFiles: 'Your terms',
dataAccessPlace: 'Place'
})

const dataset = await getDataset.execute(
ids.numericId,
DatasetNotNumberedVersion.LATEST,
false,
false
)

expect(dataset.termsOfUse.termsOfAccess.fileAccessRequest).toBe(true)
expect(dataset.termsOfUse.termsOfAccess.termsOfAccessForRestrictedFiles).toBe('Your terms')
expect(dataset.termsOfUse.termsOfAccess.dataAccessPlace).toBe('Place')
})

test('should throw when dataset does not exist', async () => {
await expect(
updateTermsOfAccess.execute(999999, {
fileAccessRequest: false
})
).rejects.toBeInstanceOf(WriteError)
})
})
Loading