Skip to content

Commit fda27f7

Browse files
authored
Merge pull request #151 from IQSS/146-edit-dataset-use-case
Edit dataset use case
2 parents d3f0113 + e7714d5 commit fda27f7

31 files changed

+1380
-749
lines changed

docs/localDevelopment.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,14 @@ These environment variables can be updated as needed for integration testing. Fo
7272
npm run test:coverage
7373
```
7474

75+
### Display container logs while running tests
76+
77+
If you want to see the container logs while running integration or functional tests, run the following command before running the test commands:
78+
79+
```bash
80+
export DEBUG=testcontainers:containers npm test
81+
```
82+
7583
## Format and lint
7684

7785
### Run formatter

docs/useCases.md

Lines changed: 65 additions & 6 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+
- [Update a Dataset](#update-a-dataset)
2627
- [Publish a Dataset](#publish-a-dataset)
2728
- [Files](#Files)
2829
- [Files read use cases](#files-read-use-cases)
@@ -299,7 +300,7 @@ The `DatasetPreviewSubset`returned instance contains a property called `totalDat
299300

300301
#### Create a Dataset
301302

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

304305
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.
305306

@@ -310,7 +311,7 @@ import { createDataset } from '@iqss/dataverse-client-javascript'
310311

311312
/* ... */
312313

313-
const newDatasetDTO: NewDatasetDTO = {
314+
const datasetDTO: DatasetDTO = {
314315
metadataBlockValues: [
315316
{
316317
name: 'citation',
@@ -345,7 +346,7 @@ const newDatasetDTO: NewDatasetDTO = {
345346
]
346347
}
347348

348-
createDataset.execute(newDatasetDTO).then((newDatasetIds: CreatedDatasetIdentifiers) => {
349+
createDataset.execute(datasetDTO).then((newDatasetIds: CreatedDatasetIdentifiers) => {
349350
/* ... */
350351
})
351352

@@ -358,6 +359,66 @@ The above example creates the new dataset in the `root` collection since no coll
358359

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

362+
#### Update a Dataset
363+
364+
Updates an existing Dataset, given a [DatasetDTO](../src/datasets/domain/dtos/DatasetDTO.ts) with the updated information.
365+
366+
If a draft of the dataset already exists, the metadata of that draft is overwritten; otherwise, a new draft is created with the updated metadata.
367+
368+
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.
369+
370+
##### Example call:
371+
372+
```typescript
373+
import { updateDataset } from '@iqss/dataverse-client-javascript'
374+
375+
/* ... */
376+
377+
const datasetId = 1
378+
const datasetDTO: DatasetDTO = {
379+
metadataBlockValues: [
380+
{
381+
name: 'citation',
382+
fields: {
383+
title: 'Updated Dataset',
384+
author: [
385+
{
386+
authorName: 'John Doe',
387+
authorAffiliation: 'Dataverse'
388+
},
389+
{
390+
authorName: 'John Lee',
391+
authorAffiliation: 'Dataverse'
392+
}
393+
],
394+
datasetContact: [
395+
{
396+
datasetContactEmail: '[email protected]',
397+
datasetContactName: 'John'
398+
}
399+
],
400+
dsDescription: [
401+
{
402+
dsDescriptionValue: 'This is the description of our new dataset'
403+
}
404+
],
405+
subject: 'Earth and Environmental Sciences'
406+
407+
/* Rest of field values... */
408+
}
409+
}
410+
]
411+
}
412+
413+
updateDataset.execute(datasetId, datasetDTO)
414+
415+
/* ... */
416+
```
417+
418+
_See [use case](../src/datasets/domain/useCases/UpdateDataset.ts) implementation_.
419+
420+
The `datasetId` parameter can be a string, for persistent identifiers, or a number, for numeric identifiers.
421+
361422
#### Publish a Dataset
362423

363424
Publishes a Dataset, given its identifier and the type of version update to perform.
@@ -372,9 +433,7 @@ import { publishDataset } from '@iqss/dataverse-client-javascript'
372433
const datasetId = 'doi:10.77777/FK2/AAAAAA'
373434
const versionUpdateType = VersionUpdateType.MINOR
374435

375-
publishDataset.execute(datasetId, versionUpdateType).then((publishedDataset: Dataset) => {
376-
/* ... */
377-
})
436+
publishDataset.execute(datasetId, versionUpdateType)
378437

379438
/* ... */
380439
```
Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
11
export interface UseCase<T> {
2-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
3-
execute(...args: any[]): Promise<T>
2+
execute(...args: unknown[]): Promise<T>
43
}

src/core/domain/useCases/validators/NewResourceValidator.ts

Lines changed: 0 additions & 4 deletions
This file was deleted.
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
export interface ResourceValidator {
2+
validate(...args: unknown[]): void
3+
}

src/core/infra/repositories/ApiRepository.ts

Lines changed: 27 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -22,16 +22,15 @@ export abstract class ApiRepository {
2222
data: string | object,
2323
queryParams: object = {}
2424
): Promise<AxiosResponse> {
25-
return await axios
26-
.post(
27-
this.buildRequestUrl(apiEndpoint),
28-
JSON.stringify(data),
29-
this.buildRequestConfig(true, queryParams)
30-
)
31-
.then((response) => response)
32-
.catch((error) => {
33-
throw new WriteError(this.buildErrorMessage(error))
34-
})
25+
return await this.doRequest('post', apiEndpoint, data, queryParams)
26+
}
27+
28+
public async doPut(
29+
apiEndpoint: string,
30+
data: string | object,
31+
queryParams: object = {}
32+
): Promise<AxiosResponse> {
33+
return await this.doRequest('put', apiEndpoint, data, queryParams)
3534
}
3635

3736
protected buildApiEndpoint(
@@ -83,4 +82,22 @@ export abstract class ApiRepository {
8382
const message = error.response && error.response.data ? ` ${error.response.data.message}` : ''
8483
return `[${status}]${message}`
8584
}
85+
86+
private async doRequest(
87+
method: 'post' | 'put',
88+
apiEndpoint: string,
89+
data: string | object,
90+
queryParams: object = {}
91+
): Promise<AxiosResponse> {
92+
const requestData = JSON.stringify(data)
93+
const requestUrl = this.buildRequestUrl(apiEndpoint)
94+
const requestConfig = this.buildRequestConfig(true, queryParams)
95+
96+
try {
97+
const response = await axios[method](requestUrl, requestData, requestConfig)
98+
return response
99+
} catch (error) {
100+
throw new WriteError(this.buildErrorMessage(error))
101+
}
102+
}
86103
}
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
import { DatasetLicense } from '../models/Dataset'
2+
3+
export interface DatasetDTO {
4+
license?: DatasetLicense
5+
metadataBlockValues: DatasetMetadataBlockValuesDTO[]
6+
}
7+
8+
export interface DatasetMetadataBlockValuesDTO {
9+
name: string
10+
fields: DatasetMetadataFieldsDTO
11+
}
12+
13+
export type DatasetMetadataFieldsDTO = Record<string, DatasetMetadataFieldValueDTO>
14+
15+
export type DatasetMetadataFieldValueDTO =
16+
| string
17+
| string[]
18+
| DatasetMetadataChildFieldValueDTO
19+
| DatasetMetadataChildFieldValueDTO[]
20+
21+
export type DatasetMetadataChildFieldValueDTO = Record<string, string>

src/datasets/domain/dtos/NewDatasetDTO.ts

Lines changed: 0 additions & 21 deletions
This file was deleted.

src/datasets/domain/repositories/IDatasetsRepository.ts

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import { DatasetLock } from '../models/DatasetLock'
33
import { DatasetPreviewSubset } from '../models/DatasetPreviewSubset'
44
import { DatasetUserPermissions } from '../models/DatasetUserPermissions'
55
import { CreatedDatasetIdentifiers } from '../models/CreatedDatasetIdentifiers'
6-
import { NewDatasetDTO } from '../dtos/NewDatasetDTO'
6+
import { DatasetDTO } from '../dtos/DatasetDTO'
77
import { MetadataBlock } from '../../../metadataBlocks'
88

99
export interface IDatasetsRepository {
@@ -28,9 +28,14 @@ export interface IDatasetsRepository {
2828
getPrivateUrlDatasetCitation(token: string): Promise<string>
2929
getDatasetUserPermissions(datasetId: number | string): Promise<DatasetUserPermissions>
3030
createDataset(
31-
newDataset: NewDatasetDTO,
31+
newDataset: DatasetDTO,
3232
datasetMetadataBlocks: MetadataBlock[],
3333
collectionId: string
3434
): Promise<CreatedDatasetIdentifiers>
3535
publishDataset(datasetId: number | string, versionUpdateType: VersionUpdateType): Promise<void>
36+
updateDataset(
37+
datasetId: number | string,
38+
dataset: DatasetDTO,
39+
datasetMetadataBlocks: MetadataBlock[]
40+
): Promise<void>
3641
}
Lines changed: 11 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -1,59 +1,36 @@
1-
import { UseCase } from '../../../core/domain/useCases/UseCase'
21
import { IDatasetsRepository } from '../repositories/IDatasetsRepository'
3-
import { NewDatasetDTO, NewDatasetMetadataBlockValuesDTO } from '../dtos/NewDatasetDTO'
4-
import { NewResourceValidator } from '../../../core/domain/useCases/validators/NewResourceValidator'
2+
import { DatasetDTO } from '../dtos/DatasetDTO'
3+
import { ResourceValidator } from '../../../core/domain/useCases/validators/ResourceValidator'
54
import { IMetadataBlocksRepository } from '../../../metadataBlocks/domain/repositories/IMetadataBlocksRepository'
6-
import { MetadataBlock } from '../../../metadataBlocks'
75
import { CreatedDatasetIdentifiers } from '../models/CreatedDatasetIdentifiers'
86
import { ROOT_COLLECTION_ALIAS } from '../../../collections/domain/models/Collection'
7+
import { DatasetWriteUseCase } from './DatasetWriteUseCase'
98

10-
export class CreateDataset implements UseCase<CreatedDatasetIdentifiers> {
11-
private datasetsRepository: IDatasetsRepository
12-
private metadataBlocksRepository: IMetadataBlocksRepository
13-
private newDatasetValidator: NewResourceValidator
14-
9+
export class CreateDataset extends DatasetWriteUseCase<CreatedDatasetIdentifiers> {
1510
constructor(
1611
datasetsRepository: IDatasetsRepository,
1712
metadataBlocksRepository: IMetadataBlocksRepository,
18-
newDatasetValidator: NewResourceValidator
13+
newDatasetValidator: ResourceValidator
1914
) {
20-
this.datasetsRepository = datasetsRepository
21-
this.metadataBlocksRepository = metadataBlocksRepository
22-
this.newDatasetValidator = newDatasetValidator
15+
super(datasetsRepository, metadataBlocksRepository, newDatasetValidator)
2316
}
2417

2518
/**
26-
* Creates a new Dataset in a collection, given a NewDatasetDTO object and an optional collection identifier, which defaults to root.
19+
* Creates a new Dataset in a collection, given a DatasetDTO object and an optional collection identifier, which defaults to root.
2720
*
28-
* @param {NewDatasetDTO} [newDataset] - NewDatasetDTO object including the new dataset metadata field values for each metadata block.
21+
* @param {DatasetDTO} [newDataset] - DatasetDTO object including the new dataset metadata field values for each metadata block.
2922
* @param {string} [collectionId] - Specifies the collection identifier where the new dataset should be created (optional, defaults to root).
3023
* @returns {Promise<CreatedDatasetIdentifiers>}
3124
* @throws {ResourceValidationError} - If there are validation errors related to the provided information.
3225
* @throws {ReadError} - If there are errors while reading data.
3326
* @throws {WriteError} - If there are errors while writing data.
3427
*/
3528
async execute(
36-
newDataset: NewDatasetDTO,
29+
newDataset: DatasetDTO,
3730
collectionId = ROOT_COLLECTION_ALIAS
3831
): Promise<CreatedDatasetIdentifiers> {
3932
const metadataBlocks = await this.getNewDatasetMetadataBlocks(newDataset)
40-
41-
this.newDatasetValidator.validate(newDataset, metadataBlocks)
42-
43-
return this.datasetsRepository.createDataset(newDataset, metadataBlocks, collectionId)
44-
}
45-
46-
async getNewDatasetMetadataBlocks(newDataset: NewDatasetDTO): Promise<MetadataBlock[]> {
47-
const metadataBlocks: MetadataBlock[] = []
48-
await Promise.all(
49-
newDataset.metadataBlockValues.map(
50-
async (metadataBlockValue: NewDatasetMetadataBlockValuesDTO) => {
51-
metadataBlocks.push(
52-
await this.metadataBlocksRepository.getMetadataBlockByName(metadataBlockValue.name)
53-
)
54-
}
55-
)
56-
)
57-
return metadataBlocks
33+
this.getNewDatasetValidator().validate(newDataset, metadataBlocks)
34+
return this.getDatasetsRepository().createDataset(newDataset, metadataBlocks, collectionId)
5835
}
5936
}

0 commit comments

Comments
 (0)