Skip to content
Merged
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,9 @@ This changelog follows the principles of [Keep a Changelog](https://keepachangel

### Added

- Guestbooks: Added `getGuestbookResponsesByGuestbookId` use case and repository support for retrieving paginated guestbook responses with total count as structured JSON.
- Guestbooks: Added `downloadGuestbookResponsesByCollectionId` and `downloadGuestbookResponsesOfAGuestbook` use cases and repository support for exporting guestbook responses as raw CSV content.
- Guestbooks: Added optional `includeStats` support to `getGuestbooksByCollectionId`, returning `usageCount` and `responseCount` when requested.
- Files: Added `getFileCitationByFormat` use case, repository method, and `FileCitationFormat` enum to support Dataverse file citation exports in `EndNote`, `RIS`, `BibTeX`, `CSL`, and `Internal` formats.
- Collections: Added `allowedDatasetTypes` field to the [Collection](./src/collections/domain/models/Collection.ts) model. This field is optional and only populated the feature is enabled on the installation and configured on the collection.
- Collections: Added theme information when retrieving a collection using `getCollection`.
Expand Down
76 changes: 73 additions & 3 deletions docs/useCases.md
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,9 @@ The different use cases currently available in the package are classified below,
- [Guestbooks read use cases](#guestbooks-read-use-cases)
- [Get a Guestbook](#get-a-guestbook)
- [Get Guestbooks By Collection Id](#get-guestbooks-by-collection-id)
- [Get Guestbook Responses By Guestbook Id](#get-guestbook-responses-by-guestbook-id)
- [Download Guestbook Responses By Collection Id](#download-guestbook-responses-by-collection-id)
- [Download Guestbook Responses Of A Guestbook](#download-guestbook-responses-of-a-guestbook)
- [Guestbooks write use cases](#guestbooks-write-use-cases)
- [Create a Guestbook](#create-a-guestbook)
- [Set Guestbook Enabled](#set-guestbook-enabled)
Expand Down Expand Up @@ -3168,21 +3171,88 @@ _See [use case](../src/guestbooks/domain/useCases/GetGuestbook.ts) implementatio
#### Get Guestbooks By Collection Id

Returns all [Guestbook](../src/guestbooks/domain/models/Guestbook.ts) entries available for a collection.
Set `includeStats` to `true` to include `usageCount` and `responseCount` for each guestbook.

##### Example call:

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

const collectionIdOrAlias = 'root'
const includeStats = true

getGuestbooksByCollectionId.execute(collectionIdOrAlias).then((guestbooks: Guestbook[]) => {
/* ... */
})
getGuestbooksByCollectionId
.execute(collectionIdOrAlias, includeStats)
.then((guestbooks: Guestbook[]) => {
/* ... */
})
```

_See [use case](../src/guestbooks/domain/useCases/GetGuestbooksByCollectionId.ts) implementation_.

#### Get Guestbook Responses By Guestbook Id

Returns a [GuestbookResponseSubset](../src/guestbooks/domain/models/GuestbookResponse.ts) containing paginated guestbook response entries and the total response count for a guestbook.

##### Example call:

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

const guestbookId = 123
const limit = 10
const offset = 0

getGuestbookResponsesByGuestbookId

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The API will return pagination info as well, so the return type should be GustbookSubset, containing the array of Guestbook items, and the total count, similar to how we handle pagination for other objects

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added. Thanks for catching this😃

.execute(guestbookId, limit, offset)
.then((guestbookResponseSubset: GuestbookResponseSubset) => {
/* ... */
})
```

_See [use case](../src/guestbooks/domain/useCases/GetGuestbookResponsesByGuestbookId.ts) implementation_.

#### Download Guestbook Responses By Collection Id

Downloads all guestbook responses for a collection and returns the raw response body, typically CSV content.

##### Example call:

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

const collectionIdOrAlias = 'root'

downloadGuestbookResponsesByCollectionId
.execute(collectionIdOrAlias)
.then((csvResponse: string) => {
/* ... */
})
```

_See [use case](../src/guestbooks/domain/useCases/DownloadGuestbookResponsesByCollectionId.ts) implementation_.

#### Download Guestbook Responses Of A Guestbook

Downloads guestbook responses for one guestbook in a collection and returns the raw response body, typically CSV content.

##### Example call:

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

const collectionIdOrAlias = 'root'
const guestbookId = 123

downloadGuestbookResponsesOfAGuestbook
.execute(collectionIdOrAlias, guestbookId)
.then((csvResponse: string) => {
/* ... */
})
```

_See [use case](../src/guestbooks/domain/useCases/DownloadGuestbookResponsesOfAGuestbook.ts) implementation_.

### Guestbooks Write Use Cases

#### Create a Guestbook
Expand Down
13 changes: 13 additions & 0 deletions src/guestbooks/domain/dtos/GuestbookResponsesDTO.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import { Guestbook } from '../models/Guestbook'
import { GuestbookResponse } from '../models/GuestbookResponse'

export interface GuestbookResponsesDTO {
guestbook: Guestbook
responses: GuestbookResponse[]
pagination?: GuestbookResponsesPaginationDTO
}

export interface GuestbookResponsesPaginationDTO {
next?: string
totalResponses: number
}
2 changes: 2 additions & 0 deletions src/guestbooks/domain/models/Guestbook.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,4 +25,6 @@ export interface Guestbook {
customQuestions: GuestbookCustomQuestion[]
createTime: string
dataverseId: number
usageCount?: number
responseCount?: number
}
32 changes: 32 additions & 0 deletions src/guestbooks/domain/models/GuestbookResponse.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
export interface GuestbookResponse {
id: number
dataset: string
datasetPid: string
date: string
type: EventType
fileName?: string
fileId?: number
filePid?: string
userName: string
email?: string
institution?: string
position?: string
customQuestions?: GuestbookResponseCustomQuestion[]
}

export interface GuestbookResponseCustomQuestion {
question: string
response: string
}

export interface GuestbookResponseSubset {
guestbookResponses: GuestbookResponse[]
totalGuestbookResponseCount: number
}

export enum EventType {
ACCESS_REQUEST = 'AccessRequest',
DOWNLOAD = 'Download',
SUBSET = 'Subset',
EXPLORE = 'Explore'
}
16 changes: 15 additions & 1 deletion src/guestbooks/domain/repositories/IGuestbooksRepository.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,27 @@
import { CreateGuestbookDTO } from '../dtos/CreateGuestbookDTO'
import { Guestbook } from '../models/Guestbook'
import { GuestbookResponseSubset } from '../models/GuestbookResponse'

export interface IGuestbooksRepository {
createGuestbook(
collectionIdOrAlias: number | string,
guestbook: CreateGuestbookDTO
): Promise<number>
getGuestbook(guestbookId: number): Promise<Guestbook>
getGuestbooksByCollectionId(collectionIdOrAlias: number | string): Promise<Guestbook[]>
getGuestbooksByCollectionId(
collectionIdOrAlias: number | string,
includeStats?: boolean,
includeInherited?: boolean
): Promise<Guestbook[]>
getGuestbookResponsesByGuestbookId(
guestbookId: number,
limit?: number,
offset?: number
): Promise<GuestbookResponseSubset>
downloadGuestbookResponsesByCollectionId(
collectionIdOrAlias: number | string,
guestbookId?: number
): Promise<string>
setGuestbookEnabled(
collectionIdOrAlias: number | string,
guestbookId: number,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { UseCase } from '../../../core/domain/useCases/UseCase'
import { IGuestbooksRepository } from '../repositories/IGuestbooksRepository'

export class DownloadGuestbookResponsesByCollectionId implements UseCase<string> {
constructor(private readonly guestbooksRepository: IGuestbooksRepository) {}

/**
* Downloads all guestbook responses for a collection.
*
* @param {number | string} collectionIdOrAlias - Collection alias/identifier or numeric database id.
* @returns {Promise<string>} Raw response body returned by the Dataverse API.
*/
async execute(collectionIdOrAlias: number | string): Promise<string> {
return await this.guestbooksRepository.downloadGuestbookResponsesByCollectionId(
collectionIdOrAlias
)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import { UseCase } from '../../../core/domain/useCases/UseCase'
import { IGuestbooksRepository } from '../repositories/IGuestbooksRepository'

export class DownloadGuestbookResponsesOfAGuestbook implements UseCase<string> {
constructor(private readonly guestbooksRepository: IGuestbooksRepository) {}

/**
Comment thread
ChengShi-1 marked this conversation as resolved.
* Downloads guestbook responses for one guestbook in a collection.
*
* @param {number | string} collectionIdOrAlias - Collection alias/identifier or numeric database id.
* @param {number} guestbookId - Guestbook identifier to restrict the export.
* @returns {Promise<string>} Raw response body returned by the Dataverse API.
*/
async execute(collectionIdOrAlias: number | string, guestbookId: number): Promise<string> {
return await this.guestbooksRepository.downloadGuestbookResponsesByCollectionId(
collectionIdOrAlias,
guestbookId
)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import { UseCase } from '../../../core/domain/useCases/UseCase'
import { GuestbookResponseSubset } from '../models/GuestbookResponse'
import { IGuestbooksRepository } from '../repositories/IGuestbooksRepository'

export class GetGuestbookResponsesByGuestbookId implements UseCase<GuestbookResponseSubset> {
constructor(private readonly guestbooksRepository: IGuestbooksRepository) {}

/**
* Returns paginated guestbook responses for one guestbook.
*
* @param {number} guestbookId - Guestbook identifier.
* @param {number} limit - Maximum number of responses to return.
* @param {number} offset - Number of responses to skip.
* @returns {Promise<GuestbookResponseSubset>}
*/
async execute(guestbookId: number, limit = 10, offset = 0): Promise<GuestbookResponseSubset> {
return await this.guestbooksRepository.getGuestbookResponsesByGuestbookId(
guestbookId,
limit,
offset
)
}
}
18 changes: 16 additions & 2 deletions src/guestbooks/domain/useCases/GetGuestbooksByCollectionId.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,23 @@ export class GetGuestbooksByCollectionId implements UseCase<Guestbook[]> {
* Returns all guestbooks available for a given collection.
*
* @param {number | string} collectionIdOrAlias - Collection identifier (numeric id or alias).
* @param {boolean} [includeStats=false] - Include usage and response counts for each guestbook.
* @param {boolean} [includeInherited=false] - Include guestbooks inherited from hierarchical owners.
* @returns {Promise<Guestbook[]>}
*/
async execute(collectionIdOrAlias: number | string): Promise<Guestbook[]> {
return await this.guestbooksRepository.getGuestbooksByCollectionId(collectionIdOrAlias)
async execute(
collectionIdOrAlias: number | string,
includeStats = false,
includeInherited = false
): Promise<Guestbook[]> {
if (!includeStats && !includeInherited) {
return await this.guestbooksRepository.getGuestbooksByCollectionId(collectionIdOrAlias)
}

return await this.guestbooksRepository.getGuestbooksByCollectionId(
collectionIdOrAlias,
includeStats,
includeInherited
)
}
}
25 changes: 25 additions & 0 deletions src/guestbooks/index.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
import { GuestbooksRepository } from './infra/repositories/GuestbooksRepository'
import { CreateGuestbook } from './domain/useCases/CreateGuestbook'
import { DownloadGuestbookResponsesByCollectionId } from './domain/useCases/DownloadGuestbookResponsesByCollectionId'
import { DownloadGuestbookResponsesOfAGuestbook } from './domain/useCases/DownloadGuestbookResponsesOfAGuestbook'
import { GetGuestbook } from './domain/useCases/GetGuestbook'
import { GetGuestbookResponsesByGuestbookId } from './domain/useCases/GetGuestbookResponsesByGuestbookId'
import { GetGuestbooksByCollectionId } from './domain/useCases/GetGuestbooksByCollectionId'
import { SetGuestbookEnabled } from './domain/useCases/SetGuestbookEnabled'
import { AssignDatasetGuestbook } from './domain/useCases/AssignDatasetGuestbook'
Expand All @@ -9,15 +12,27 @@ import { RemoveDatasetGuestbook } from './domain/useCases/RemoveDatasetGuestbook
const guestbooksRepository = new GuestbooksRepository()

const createGuestbook = new CreateGuestbook(guestbooksRepository)
const downloadGuestbookResponsesByCollectionId = new DownloadGuestbookResponsesByCollectionId(
guestbooksRepository
)
const downloadGuestbookResponsesOfAGuestbook = new DownloadGuestbookResponsesOfAGuestbook(
guestbooksRepository
)
const getGuestbook = new GetGuestbook(guestbooksRepository)
const getGuestbookResponsesByGuestbookId = new GetGuestbookResponsesByGuestbookId(
guestbooksRepository
)
const getGuestbooksByCollectionId = new GetGuestbooksByCollectionId(guestbooksRepository)
const setGuestbookEnabled = new SetGuestbookEnabled(guestbooksRepository)
const assignDatasetGuestbook = new AssignDatasetGuestbook(guestbooksRepository)
const removeDatasetGuestbook = new RemoveDatasetGuestbook(guestbooksRepository)

export {
createGuestbook,
downloadGuestbookResponsesByCollectionId,
downloadGuestbookResponsesOfAGuestbook,
getGuestbook,
getGuestbookResponsesByGuestbookId,
getGuestbooksByCollectionId,
setGuestbookEnabled,
assignDatasetGuestbook,
Expand All @@ -29,4 +44,14 @@ export {
CreateGuestbookCustomQuestionDTO,
CreateGuestbookOptionDTO
} from './domain/dtos/CreateGuestbookDTO'
export {
GuestbookResponsesDTO,
GuestbookResponsesPaginationDTO
} from './domain/dtos/GuestbookResponsesDTO'
export { Guestbook, GuestbookCustomQuestion, GuestbookOption } from './domain/models/Guestbook'
export {
EventType,
GuestbookResponse,
GuestbookResponseCustomQuestion,
GuestbookResponseSubset
} from './domain/models/GuestbookResponse'
Loading
Loading