Skip to content
Merged
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: 2 additions & 2 deletions docs/useCases.md
Original file line number Diff line number Diff line change
Expand Up @@ -860,7 +860,7 @@ _See [use case](../src/datasets/domain/useCases/GetDatasetAvailableDatasetType.t

#### Create a Dataset

Creates a new Dataset in a collection, given a [DatasetDTO](../src/datasets/domain/dtos/DatasetDTO.ts) object and an optional collection identifier, which defaults to `:root`.
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.

This use case validates the submitted fields of each metadata block and can return errors of type [ResourceValidationError](../src/core/domain/useCases/validators/errors/ResourceValidationError.ts), which include sufficient information to determine which field value is invalid and why.

Expand Down Expand Up @@ -915,7 +915,7 @@ createDataset.execute(datasetDTO).then((newDatasetIds: CreatedDatasetIdentifiers

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

The above example creates the new dataset in the root collection since no collection identifier is specified. If you want to create the dataset in a different collection, you must add the collection identifier as a second parameter in the use case call.
The above example creates the new dataset in the root collection since no collection identifier is specified. If you want to create the dataset in a different collection, you must add the collection identifier as a second parameter in the use case call. If you want the dataset type to be anything other than dataset, first [check available dataset types](#get-dataset-available-dataset-types) and then add the name of the dataset type as the third parameter.

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

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 @@ -14,6 +14,7 @@ export interface Dataset {
citationDate?: string
metadataBlocks: DatasetMetadataBlocks
isPartOf: DvObjectOwnerNode
datasetType?: string
}

export interface DatasetVersionInfo {
Expand Down
3 changes: 2 additions & 1 deletion src/datasets/domain/repositories/IDatasetsRepository.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,8 @@ export interface IDatasetsRepository {
createDataset(
newDataset: DatasetDTO,
datasetMetadataBlocks: MetadataBlock[],
collectionId: string
collectionId: string,
datasetType?: string
): Promise<CreatedDatasetIdentifiers>
publishDataset(datasetId: number | string, versionUpdateType: VersionUpdateType): Promise<void>
updateDataset(
Expand Down
11 changes: 9 additions & 2 deletions src/datasets/domain/useCases/CreateDataset.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,17 +20,24 @@ export class CreateDataset extends DatasetWriteUseCase<CreatedDatasetIdentifiers
*
* @param {DatasetDTO} [newDataset] - DatasetDTO object including the new dataset metadata field values for each metadata block.
* @param {string} [collectionId] - Specifies the collection identifier where the new dataset should be created (optional, defaults to :root).
* @param {string} [datasetType] - Specifies the dataset type (optional, when omitted, defaults to "dataset").
* @returns {Promise<CreatedDatasetIdentifiers>}
* @throws {ResourceValidationError} - If there are validation errors related to the provided information.
* @throws {ReadError} - If there are errors while reading data.
* @throws {WriteError} - If there are errors while writing data.
*/
async execute(
newDataset: DatasetDTO,
collectionId = ROOT_COLLECTION_ID
collectionId = ROOT_COLLECTION_ID,
datasetType?: string
): Promise<CreatedDatasetIdentifiers> {
const metadataBlocks = await this.getNewDatasetMetadataBlocks(newDataset)
this.getNewDatasetValidator().validate(newDataset, metadataBlocks)
return this.getDatasetsRepository().createDataset(newDataset, metadataBlocks, collectionId)
return this.getDatasetsRepository().createDataset(
newDataset,
metadataBlocks,
collectionId,
datasetType
)
}
}
9 changes: 7 additions & 2 deletions src/datasets/infra/repositories/DatasetsRepository.ts
Original file line number Diff line number Diff line change
Expand Up @@ -208,11 +208,16 @@ export class DatasetsRepository extends ApiRepository implements IDatasetsReposi
public async createDataset(
newDataset: DatasetDTO,
datasetMetadataBlocks: MetadataBlock[],
collectionId: string
collectionId: string,
datasetType?: string
): Promise<CreatedDatasetIdentifiers> {
return this.doPost(
`/dataverses/${collectionId}/datasets`,
transformDatasetModelToNewDatasetRequestPayload(newDataset, datasetMetadataBlocks)
transformDatasetModelToNewDatasetRequestPayload(
newDataset,
datasetMetadataBlocks,
datasetType
)
)
.then((response) => {
const responseData = response.data.data
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ export interface DatasetPayload {
files: FilePayload[]
isPartOf: OwnerNodePayload
deaccessionNote?: string
datasetType?: string
}

export interface DatasetLicensePayload {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ import { MetadataBlock, MetadataFieldInfo } from '../../../../metadataBlocks'
const turndownService = new TurndownService()

export interface NewDatasetRequestPayload {
datasetType?: string
datasetVersion: {
license?: DatasetLicense
metadataBlocks: Record<string, MetadataBlockRequestPayload>
Expand Down Expand Up @@ -96,9 +97,11 @@ export const transformDatasetModelToUpdateDatasetRequestPayload = (

export const transformDatasetModelToNewDatasetRequestPayload = (
dataset: DatasetDTO,
metadataBlocks: MetadataBlock[]
metadataBlocks: MetadataBlock[],
datasetType?: string
): NewDatasetRequestPayload => {
return {
datasetType,
datasetVersion: {
...(dataset.license && { license: dataset.license }),
metadataBlocks: transformMetadataBlockModelsToRequestPayload(
Expand Down Expand Up @@ -293,6 +296,9 @@ export const transformVersionPayloadToDataset = (
if ('citationDate' in versionPayload) {
datasetModel.citationDate = versionPayload.citationDate
}
if ('datasetType' in versionPayload) {
datasetModel.datasetType = versionPayload.datasetType
}
return datasetModel
}

Expand Down
102 changes: 101 additions & 1 deletion test/functional/datasets/CreateDataset.test.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { createDataset, DatasetDTO } from '../../../src/datasets'
import { ApiConfig } from '../../../src'
import { ApiConfig, WriteError } from '../../../src'
import { TestConstants } from '../../testHelpers/TestConstants'
import { DataverseApiAuthMechanism } from '../../../src/core/infra/repositories/ApiConfig'
import { FieldValidationError } from '../../../src/datasets/domain/useCases/validators/errors/FieldValidationError'
Expand Down Expand Up @@ -61,6 +61,58 @@ describe('execute', () => {
}
})

test('should successfully create a new dataset when a valid dataset type is sent', async () => {
const testNewDataset = {
metadataBlockValues: [
{
name: 'citation',
fields: {
title: 'Dataset created using the createDataset use case',
author: [
{
authorName: 'Admin, Dataverse',
authorAffiliation: 'Dataverse.org'
},
{
authorName: 'Owner, Dataverse',
authorAffiliation: 'Dataversedemo.org'
}
],
datasetContact: [
{
datasetContactEmail: 'finch@mailinator.com',
datasetContactName: 'Finch, Fiona'
}
],
dsDescription: [
{
dsDescriptionValue: 'This is the description of the dataset.'
}
],
subject: ['Medicine, Health and Life Sciences']
}
}
]
}
expect.assertions(3)

try {
const defaultDatasetType = 'dataset'
const createdDatasetIdentifiers = await createDataset.execute(
testNewDataset,
':root',
defaultDatasetType
)

expect(createdDatasetIdentifiers).not.toBeNull()
expect(createdDatasetIdentifiers.numericId).not.toBeNull()
expect(createdDatasetIdentifiers.persistentId).not.toBeNull()
await deleteUnpublishedDatasetViaApi(createdDatasetIdentifiers.numericId)
} catch (error) {
throw new Error('Dataset should be created')
}
})

test('should throw an error when a first level required field is missing', async () => {
const testNewDataset = {
metadataBlockValues: [
Expand Down Expand Up @@ -213,4 +265,52 @@ describe('execute', () => {
)
}
})

test('should throw an error when an invalid dataset type is sent', async () => {
const testNewDataset = {
metadataBlockValues: [
{
name: 'citation',
fields: {
title: 'Dataset created using the createDataset use case',
author: [
{
authorName: 'Admin, Dataverse',
authorAffiliation: 'Dataverse.org'
},
{
authorName: 'Owner, Dataverse',
authorAffiliation: 'Dataversedemo.org'
}
],
datasetContact: [
{
datasetContactEmail: 'finch@mailinator.com',
datasetContactName: 'Finch, Fiona'
}
],
dsDescription: [
{
dsDescriptionValue: 'This is the description of the dataset.'
}
],
subject: ['Medicine, Health and Life Sciences']
}
}
]
}
expect.assertions(1)
let writeError: WriteError | undefined = undefined
try {
const invalidDatasetType = 'doesNotExist'
await createDataset.execute(testNewDataset, ':root', invalidDatasetType)
throw new Error('Use case should throw an error')
} catch (error) {
writeError = error as WriteError
} finally {
expect(writeError?.message).toEqual(
'There was an error when writing the resource. Reason was: [400] Error parsing Json: Invalid dataset type: doesNotExist'
)
}
})
})
59 changes: 59 additions & 0 deletions test/integration/datasets/DatasetsRepository.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,7 @@

const filesRepositorySut = new FilesRepository()
const directUploadSut: DirectUploadClient = new DirectUploadClient(filesRepositorySut)
const defaultDatasetType = 'dataset'

beforeAll(async () => {
ApiConfig.init(
Expand Down Expand Up @@ -827,6 +828,64 @@
expect(actualCreatedDataset.metadataBlocks[0].fields.subject).toContain(
'Medicine, Health and Life Sciences'
)
// even though we didn't provide a dataset type, it should be created with the default one
expect(actualCreatedDataset.datasetType).toBe(defaultDatasetType)
})
})

describe('createDatasetWithDatasetType', () => {
test('should create a dataset with the provided dataset type', async () => {
const testNewDataset = {
metadataBlockValues: [
{
name: 'citation',
fields: {
title: 'Dataset created using the createDataset use case',
author: [
{
authorName: 'Admin, Dataverse',
authorAffiliation: 'Dataverse.org'
},
{
authorName: 'Owner, Dataverse',
authorAffiliation: 'Dataversedemo.org'
}
],
datasetContact: [
{
datasetContactEmail: 'finch@mailinator.com',
datasetContactName: 'Finch, Fiona'
}
],
dsDescription: [
{
dsDescriptionValue: 'This is the description of the dataset.'
}
],
subject: ['Medicine, Health and Life Sciences']
}
}
]
}

const metadataBlocksRepository = new MetadataBlocksRepository()
const citationMetadataBlock = await metadataBlocksRepository.getMetadataBlockByName(
'citation'
)
const createdDataset = await sut.createDataset(
testNewDataset,
[citationMetadataBlock],
ROOT_COLLECTION_ALIAS,
defaultDatasetType
)
const actualCreatedDataset = await sut.getDataset(
createdDataset.numericId,
DatasetNotNumberedVersion.LATEST,
false,
false
)

expect(actualCreatedDataset.datasetType).toBe(defaultDatasetType)
})
})

Expand Down Expand Up @@ -1074,7 +1133,7 @@
])
})
// TODO: add this test when https://github.com/IQSS/dataverse-client-javascript/issues/343 is fixed
test.skip('should throw error if trying to update an outdated internal version dataset', async () => {

Check warning on line 1136 in test/integration/datasets/DatasetsRepository.test.ts

View workflow job for this annotation

GitHub Actions / lint

Disabled test
const testDataset = {
metadataBlockValues: [
{
Expand Down
4 changes: 3 additions & 1 deletion test/testHelpers/datasets/datasetHelper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -694,9 +694,11 @@ export const createDatasetMetadataBlockModel = (): MetadataBlock => {
}

export const createNewDatasetRequestPayload = (
license?: DatasetLicense
license?: DatasetLicense,
datasetType?: string
): NewDatasetRequestPayload => {
return {
datasetType,
datasetVersion: {
...(license && { license }),
metadataBlocks: {
Expand Down
47 changes: 45 additions & 2 deletions test/unit/datasets/CreateDataset.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,49 @@ describe('execute', () => {
expect(datasetsRepositoryStub.createDataset).toHaveBeenCalledWith(
testDataset,
testMetadataBlocks,
ROOT_COLLECTION_ID
ROOT_COLLECTION_ID,
undefined
)
})

test('should return a dataset type', async () => {
const testCreatedDatasetIdentifiers: CreatedDatasetIdentifiers = {
persistentId: 'test',
numericId: 1
}

const datasetsRepositoryStub = <IDatasetsRepository>{}
datasetsRepositoryStub.createDataset = jest
.fn()
.mockResolvedValue(testCreatedDatasetIdentifiers)

const datasetValidatorStub = <ResourceValidator>{}
datasetValidatorStub.validate = jest.fn().mockResolvedValue(undefined)

const metadataBlocksRepositoryStub = <IMetadataBlocksRepository>{}
metadataBlocksRepositoryStub.getMetadataBlockByName = jest
.fn()
.mockResolvedValue(testMetadataBlocks[0])

const sut = new CreateDataset(
datasetsRepositoryStub,
metadataBlocksRepositoryStub,
datasetValidatorStub
)

const actual = await sut.execute(testDataset, ROOT_COLLECTION_ID, 'software')

expect(actual).toEqual(testCreatedDatasetIdentifiers)

expect(metadataBlocksRepositoryStub.getMetadataBlockByName).toHaveBeenCalledWith(
testMetadataBlocks[0].name
)
expect(datasetValidatorStub.validate).toHaveBeenCalledWith(testDataset, testMetadataBlocks)
expect(datasetsRepositoryStub.createDataset).toHaveBeenCalledWith(
testDataset,
testMetadataBlocks,
ROOT_COLLECTION_ID,
'software'
)
})

Expand Down Expand Up @@ -111,7 +153,8 @@ describe('execute', () => {
expect(datasetsRepositoryStub.createDataset).toHaveBeenCalledWith(
testDataset,
testMetadataBlocks,
ROOT_COLLECTION_ID
ROOT_COLLECTION_ID,
undefined
)
})

Expand Down
Loading