Skip to content

Commit 4325fe1

Browse files
committed
Merge branch 'develop' into feat/256-delete-collection-use-case
2 parents 29932e4 + e689f25 commit 4325fe1

38 files changed

Lines changed: 1448 additions & 33 deletions

docs/useCases.md

Lines changed: 121 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ The different use cases currently available in the package are classified below,
3737
- [Create a Dataset](#create-a-dataset)
3838
- [Update a Dataset](#update-a-dataset)
3939
- [Publish a Dataset](#publish-a-dataset)
40+
- [Deaccession a Dataset](#deaccession-a-dataset)
4041
- [Files](#Files)
4142
- [Files read use cases](#files-read-use-cases)
4243
- [Get a File](#get-a-file)
@@ -50,6 +51,8 @@ The different use cases currently available in the package are classified below,
5051
- [List Files in a Dataset](#list-files-in-a-dataset)
5152
- [Files write use cases](#files-write-use-cases)
5253
- [File Uploading Use Cases](#file-uploading-use-cases)
54+
- [Delete a File](#delete-a-file)
55+
- [Restrict or Unrestrict a File](#restrict-or-unrestrict-a-file)
5356
- [Metadata Blocks](#metadata-blocks)
5457
- [Metadata Blocks read use cases](#metadata-blocks-read-use-cases)
5558
- [Get All Facetable Metadata Fields](#get-all-facetable-metadata-fields)
@@ -68,6 +71,8 @@ The different use cases currently available in the package are classified below,
6871
- [Get Dataverse Backend Version](#get-dataverse-backend-version)
6972
- [Get Maximum Embargo Duration In Months](#get-maximum-embargo-duration-in-months)
7073
- [Get ZIP Download Limit](#get-zip-download-limit)
74+
- [Contact](#Contact)
75+
- [Send Feedback to Object Contacts](#send-feedback-to-object-contacts)
7176

7277
## Collections
7378

@@ -772,6 +777,35 @@ The `versionUpdateType` parameter can be a [VersionUpdateType](../src/datasets/d
772777
- `VersionUpdateType.MAJOR`
773778
- `VersionUpdateType.UPDATE_CURRENT`
774779

780+
#### Deaccession a Dataset
781+
782+
Deaccession a Dataset, given its identifier, version, and deaccessionDatasetDTO to perform.
783+
784+
##### Example call:
785+
786+
```typescript
787+
import { deaccessionDataset } from '@iqss/dataverse-client-javascript'
788+
789+
/* ... */
790+
791+
const datasetId = 1
792+
const version = ':latestPublished'
793+
const deaccessionDatasetDTO = {
794+
deaccessionReason: 'Description of the deaccession reason.',
795+
deaccessionForwardURL: 'https://demo.dataverse.org'
796+
}
797+
798+
deaccessionDataset.execute(datasetId, version, deaccessionDatasetDTO)
799+
800+
/* ... */
801+
```
802+
803+
_See [use case](../src/datasets/domain/useCases/DeaccessionDataset.ts) implementation_.
804+
The `datasetId` parameter can be a string for persistent identifiers, or a number for numeric identifiers.
805+
The `version` parameter should be a string or a [DatasetNotNumberedVersion](../src/datasets/domain/models/DatasetNotNumberedVersion.ts) enum value.
806+
807+
You cannot deaccession a dataset more than once. If you call this endpoint twice for the same dataset version, you will get a not found error on the second call, since the dataset you are looking for will no longer be published since it is already deaccessioned.
808+
775809
## Files
776810

777811
### Files read use cases
@@ -1222,6 +1256,56 @@ The following error might arise from the `AddUploadedFileToDataset` use case:
12221256

12231257
- AddUploadedFileToDatasetError: This error indicates that there was an error while adding the uploaded file to the dataset.
12241258

1259+
#### Delete a File
1260+
1261+
Deletes a File.
1262+
1263+
##### Example call:
1264+
1265+
```typescript
1266+
import { deleteFile } from '@iqss/dataverse-client-javascript'
1267+
1268+
/* ... */
1269+
1270+
const fileId = 12345
1271+
1272+
deleteFile.execute(fileId)
1273+
1274+
/* ... */
1275+
```
1276+
1277+
_See [use case](../src/files/domain/useCases/DeleteFile.ts) implementation_.
1278+
1279+
The `fileId` parameter can be a string, for persistent identifiers, or a number, for numeric identifiers.
1280+
1281+
Note that the behavior of deleting files depends on if the dataset has ever been published or not.
1282+
1283+
- If the dataset has never been published, the file will be deleted forever.
1284+
- If the dataset has published, the file is deleted from the draft (and future published versions).
1285+
- If the dataset has published, the deleted file can still be downloaded because it was part of a published version.
1286+
1287+
#### Restrict or Unrestrict a File
1288+
1289+
Restrict or unrestrict an existing file.
1290+
1291+
##### Example call:
1292+
1293+
```typescript
1294+
import { restrictFile } from '@iqss/dataverse-client-javascript'
1295+
1296+
/* ... */
1297+
1298+
const fileId = 12345
1299+
1300+
restrictFile.execute(fileId, true)
1301+
1302+
/* ... */
1303+
```
1304+
1305+
_See [use case](../src/files/domain/useCases/RestrictFile.ts) implementation_.
1306+
1307+
The `fileId` parameter can be a string, for persistent identifiers, or a number, for numeric identifiers.
1308+
12251309
## Metadata Blocks
12261310

12271311
### Metadata Blocks read use cases
@@ -1489,3 +1573,40 @@ getZipDownloadLimit.execute().then((downloadLimit: number) => {
14891573
```
14901574

14911575
_See [use case](../src/info/domain/useCases/GetZipDownloadLimit.ts) implementation_.
1576+
1577+
## Contact
1578+
1579+
#### Send Feedback to Object Contacts
1580+
1581+
Returns a [Contact](../src/contactInfo/domain/models/Contact.ts) object, which contains contact return information, showing fromEmail, subject, body.
1582+
1583+
##### Example call:
1584+
1585+
```typescript
1586+
import { submitContactInfo } from '@iqss/dataverse-client-javascript'
1587+
1588+
/* ... */
1589+
1590+
const contactDTO: ContactDTO = {
1591+
targedId: 1
1592+
subject: 'Data Question',
1593+
body: 'Please help me understand your data. Thank you!',
1594+
fromEmail: 'test@gmail.com'
1595+
}
1596+
1597+
submitContactInfo.execute(contactDTO)
1598+
1599+
/* ... */
1600+
```
1601+
1602+
_See [use case](../src/info/domain/useCases/submitContactInfo.ts) implementation_.
1603+
1604+
The above example would submit feedback to all contacts of a object where the object targetId = 1.
1605+
1606+
In ContactDTO, it takes the following information:
1607+
1608+
- **targetId**: the numeric identifier of the collection, dataset, or datafile. Persistent ids and collection aliases are not supported. (Optional)
1609+
- **identifier**: the alias of a collection or the persistence id of a dataset or datafile. (Optional)
1610+
- **subject**: the email subject line.
1611+
- **body**: the email body to send.
1612+
- **fromEmail**: the email to list in the reply-to field.

src/collections/infra/repositories/transformers/collectionTransformers.ts

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@ import {
66
CollectionPayload
77
} from './CollectionPayload'
88
import { transformPayloadToOwnerNode } from '../../../../core/infra/repositories/transformers/dvObjectOwnerNodeTransformer'
9-
import { transformHtmlToMarkdown } from '../../../../datasets/infra/repositories/transformers/datasetTransformers'
109
import { CollectionFacet } from '../../../domain/models/CollectionFacet'
1110
import { CollectionFacetPayload } from './CollectionFacetPayload'
1211
import {
@@ -53,10 +52,8 @@ const transformPayloadToCollection = (collectionPayload: CollectionPayload): Col
5352
type: collectionPayload.dataverseType as CollectionType,
5453
isMetadataBlockRoot: collectionPayload.isMetadataBlockRoot,
5554
isFacetRoot: collectionPayload.isFacetRoot,
55+
description: collectionPayload.description,
5656
childCount: collectionPayload.childCount,
57-
...(collectionPayload.description && {
58-
description: transformHtmlToMarkdown(collectionPayload.description)
59-
}),
6057
...(collectionPayload.isPartOf && {
6158
isPartOf: transformPayloadToOwnerNode(collectionPayload.isPartOf)
6259
}),
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
export interface ContactDTO {
2+
targetId?: number
3+
identifier?: string
4+
subject: string
5+
body: string
6+
fromEmail: string
7+
}
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
export interface Contact {
2+
fromEmail: string
3+
body: string
4+
subject: string
5+
}
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
import { Contact } from '../models/Contact'
2+
import { ContactDTO } from '../dtos/ContactDTO'
3+
4+
export interface IContactRepository {
5+
submitContactInfo(contactDTO: ContactDTO): Promise<Contact[]>
6+
}
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
import { UseCase } from '../../../core/domain/useCases/UseCase'
2+
import { ContactDTO } from '../dtos/ContactDTO'
3+
import { Contact } from '../models/Contact'
4+
import { IContactRepository } from '../repositories/IContactRepository'
5+
6+
export class SubmitContactInfo implements UseCase<Contact[]> {
7+
private contactRepository: IContactRepository
8+
9+
constructor(contactRepository: IContactRepository) {
10+
this.contactRepository = contactRepository
11+
}
12+
13+
/**
14+
* Submits contact information and returns a Contact model containing the submitted data.
15+
*
16+
* @param {ContactDTO} contactDTO - The contact information to be submitted.
17+
* @returns {Promise<Contact[]>} A promise resolving to a list of contact.
18+
*/
19+
20+
async execute(contactDTO: ContactDTO): Promise<Contact[]> {
21+
return await this.contactRepository.submitContactInfo(contactDTO)
22+
}
23+
}

src/contactInfo/index.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
import { SubmitContactInfo } from './domain/useCases/SubmitContactInfo'
2+
import { ContactRepository } from './infra/repositories/ContactRepository'
3+
4+
const contactRepository = new ContactRepository()
5+
const submitContactInfo = new SubmitContactInfo(contactRepository)
6+
7+
export { submitContactInfo }
8+
export { Contact } from './domain/models/Contact'
9+
export { ContactDTO } from './domain/dtos/ContactDTO'
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
import { ApiRepository } from '../../../core/infra/repositories/ApiRepository'
2+
import { Contact } from '../../domain/models/Contact'
3+
import { IContactRepository } from '../../domain/repositories/IContactRepository'
4+
import { ContactDTO } from '../../domain/dtos/ContactDTO'
5+
6+
export class ContactRepository extends ApiRepository implements IContactRepository {
7+
public async submitContactInfo(contactDTO: ContactDTO): Promise<Contact[]> {
8+
return this.doPost(`/sendfeedback`, contactDTO)
9+
.then((response) => {
10+
const responseData = response.data
11+
const contact: Contact[] = responseData.data.map((item: Contact) => ({
12+
fromEmail: item.fromEmail,
13+
subject: item.subject,
14+
body: item.body
15+
}))
16+
17+
return contact
18+
})
19+
.catch((error) => {
20+
throw error
21+
})
22+
}
23+
}

src/core/infra/repositories/ApiRepository.ts

Lines changed: 18 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ export abstract class ApiRepository {
2020

2121
public async doPost(
2222
apiEndpoint: string,
23-
data: string | object,
23+
data: string | object | boolean,
2424
queryParams: object = {},
2525
contentType: string = ApiConstants.CONTENT_TYPE_APPLICATION_JSON
2626
): Promise<AxiosResponse> {
@@ -29,7 +29,7 @@ export abstract class ApiRepository {
2929

3030
public async doPut(
3131
apiEndpoint: string,
32-
data: string | object,
32+
data: string | object | boolean,
3333
queryParams: object = {},
3434
contentType: string = ApiConstants.CONTENT_TYPE_APPLICATION_JSON
3535
): Promise<AxiosResponse> {
@@ -47,14 +47,16 @@ export abstract class ApiRepository {
4747

4848
protected buildApiEndpoint(
4949
resourceName: string,
50-
operation: string,
51-
resourceId: number | string | undefined = undefined
50+
operation?: string,
51+
resourceId?: number | string
5252
) {
53+
const operationSegment = operation ? `/${operation}` : ''
54+
5355
return typeof resourceId === 'number'
54-
? `/${resourceName}/${resourceId}/${operation}`
56+
? `/${resourceName}/${resourceId}${operationSegment}`
5557
: typeof resourceId === 'string'
56-
? `/${resourceName}/:persistentId/${operation}?persistentId=${resourceId}`
57-
: `/${resourceName}/${operation}`
58+
? `/${resourceName}/:persistentId${operationSegment}?persistentId=${resourceId}`
59+
: `/${resourceName}${operationSegment}`
5860
}
5961

6062
// eslint-disable-next-line @typescript-eslint/no-explicit-any
@@ -68,12 +70,18 @@ export abstract class ApiRepository {
6870
private async doRequest(
6971
method: 'post' | 'put',
7072
apiEndpoint: string,
71-
data: string | object,
73+
data: string | object | boolean,
7274
queryParams: object = {},
7375
contentType: string = ApiConstants.CONTENT_TYPE_APPLICATION_JSON
7476
): Promise<AxiosResponse> {
75-
const requestData =
76-
contentType == ApiConstants.CONTENT_TYPE_APPLICATION_JSON ? JSON.stringify(data) : data
77+
let requestData = data
78+
79+
if (contentType === ApiConstants.CONTENT_TYPE_APPLICATION_JSON) {
80+
if (typeof data === 'object' || typeof data === 'boolean') {
81+
requestData = JSON.stringify(data)
82+
}
83+
}
84+
7785
const requestUrl = buildRequestUrl(apiEndpoint)
7886
const requestConfig = buildRequestConfig(true, queryParams, contentType)
7987

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
export interface DatasetDeaccessionDTO {
2+
deaccessionReason: string
3+
deaccessionForwardURL?: string
4+
}

0 commit comments

Comments
 (0)