Skip to content
Merged
64 changes: 64 additions & 0 deletions docs/useCases.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ The different use cases currently available in the package are classified below,
- [List All Collection Items](#list-all-collection-items)
- [List My Data Collection Items](#list-my-data-collection-items)
- [Get Collection Featured Items](#get-collection-featured-items)
- [Get Collections for Linking](#get-collections-for-linking)
- [Collections write use cases](#collections-write-use-cases)
- [Create a Collection](#create-a-collection)
- [Update a Collection](#update-a-collection)
Expand Down Expand Up @@ -325,6 +326,69 @@ The `collectionIdOrAlias` is a generic collection identifier, which can be eithe

If no collection identifier is specified, the default collection identifier; `:root` will be used. If you want to search for a different collection, you must add the collection identifier as a parameter in the use case call.

#### Get Collections for Linking

Returns an array of [CollectionSummary](../src/collections/domain/models/CollectionSummary.ts) (id, alias, displayName) representing the Dataverse collections to which a given Dataverse collection or Dataset may be linked.

This use case supports an optional `searchTerm` to filter by collection name.

##### Example calls:

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

/* ... */

// Case 1: For a given Dataverse collection (by numeric id or alias)
const collectionIdOrAlias: number | string = 'collectionAlias' // or 123
const searchTerm = 'searchOn'

getCollectionsForLinking
.execute('collection', collectionIdOrAlias, searchTerm)
.then((collections) => {
// collections: CollectionSummary[]
/* ... */
})
.catch((error: Error) => {
/* ... */
})

/* ... */

// Case 2: For a given Dataset (by persistent identifier)
const persistentId = 'doi:10.5072/FK2/J8SJZB'

getCollectionsForLinking
.execute('dataset', persistentId, searchTerm)
.then((collections) => {
// collections: CollectionSummary[]
/* ... */
})
.catch((error: Error) => {
/* ... */
})

// Case 3: [alreadyLinked] Optional flag. When true, returns collections currently linked (candidates to unlink). Defaults to false.
const alreadyLinked = true

getCollectionsForLinking
.execute('dataset', persistentId, searchTerm, alreadyLinked)
.then((collections) => {
// collections: CollectionSummary[]
/* ... */
})
.catch((error: Error) => {
/* ... */
})
```

_See [use case](../src/collections/domain/useCases/GetCollectionsForLinking.ts) implementation_.

Notes:

- When the first argument is `'collection'`, the second argument can be a numeric collection id or a collection alias.
- When the first argument is `'dataset'`, the second argument must be the dataset persistent identifier string (e.g., `doi:...`).

### Collections Write Use Cases

#### Create a Collection
Expand Down
8 changes: 8 additions & 0 deletions src/collections/domain/repositories/ICollectionsRepository.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ import { CollectionUserPermissions } from '../models/CollectionUserPermissions'
import { PublicationStatus } from '../../../core/domain/models/PublicationStatus'
import { CollectionItemType } from '../../../collections/domain/models/CollectionItemType'
import { CollectionLinks } from '../models/CollectionLinks'
import { CollectionSummary } from '../models/CollectionSummary'
import { LinkingObjectType } from '../useCases/GetCollectionsForLinking'

export interface ICollectionsRepository {
getCollection(collectionIdOrAlias: number | string): Promise<Collection>
Expand Down Expand Up @@ -60,4 +62,10 @@ export interface ICollectionsRepository {
linkingCollectionIdOrAlias: number | string
): Promise<void>
getCollectionLinks(collectionIdOrAlias: number | string): Promise<CollectionLinks>
getCollectionsForLinking(
objectType: LinkingObjectType,
id: number | string,
searchTerm: string,
alreadyLinked: boolean
): Promise<CollectionSummary[]>
}
34 changes: 34 additions & 0 deletions src/collections/domain/useCases/GetCollectionsForLinking.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import { UseCase } from '../../../core/domain/useCases/UseCase'
import { ICollectionsRepository } from '../repositories/ICollectionsRepository'
import { CollectionSummary } from '../models/CollectionSummary'

export type LinkingObjectType = 'collection' | 'dataset'

export class GetCollectionsForLinking implements UseCase<CollectionSummary[]> {
private collectionsRepository: ICollectionsRepository

constructor(collectionsRepository: ICollectionsRepository) {
this.collectionsRepository = collectionsRepository
}

/**
* Returns an array of CollectionSummary (id, alias, displayName) to which the given Dataverse collection or Dataset may be linked.
* @param objectType - 'collection' when providing a collection identifier/alias; 'dataset' when providing a dataset persistentId.
* @param id - For objectType 'collection', a numeric id or alias string. For 'dataset', the persistentId string (e.g., doi:...)
* @param searchTerm - Optional search term to filter by collection name. Defaults to empty string (no filtering).
* @param alreadyLinked - Optional flag. When true, returns collections currently linked (candidates to unlink). Defaults to false.
*/
async execute(
objectType: LinkingObjectType,
id: number | string,
searchTerm = '',
alreadyLinked = false
): Promise<CollectionSummary[]> {
return await this.collectionsRepository.getCollectionsForLinking(
objectType,
id,
searchTerm,
alreadyLinked
)
}
}
6 changes: 5 additions & 1 deletion src/collections/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import { DeleteCollectionFeaturedItem } from './domain/useCases/DeleteCollection
import { LinkCollection } from './domain/useCases/LinkCollection'
import { UnlinkCollection } from './domain/useCases/UnlinkCollection'
import { GetCollectionLinks } from './domain/useCases/GetCollectionLinks'
import { GetCollectionsForLinking } from './domain/useCases/GetCollectionsForLinking'

const collectionsRepository = new CollectionsRepository()

Expand All @@ -34,6 +35,7 @@ const deleteCollectionFeaturedItem = new DeleteCollectionFeaturedItem(collection
const linkCollection = new LinkCollection(collectionsRepository)
const unlinkCollection = new UnlinkCollection(collectionsRepository)
const getCollectionLinks = new GetCollectionLinks(collectionsRepository)
const getCollectionsForLinking = new GetCollectionsForLinking(collectionsRepository)

export {
getCollection,
Expand All @@ -51,7 +53,8 @@ export {
deleteCollectionFeaturedItem,
linkCollection,
unlinkCollection,
getCollectionLinks
getCollectionLinks,
getCollectionsForLinking
}
export { Collection, CollectionInputLevel } from './domain/models/Collection'
export { CollectionFacet } from './domain/models/CollectionFacet'
Expand All @@ -62,3 +65,4 @@ export { CollectionItemType } from './domain/models/CollectionItemType'
export { CollectionSearchCriteria } from './domain/models/CollectionSearchCriteria'
export { FeaturedItem } from './domain/models/FeaturedItem'
export { FeaturedItemsDTO } from './domain/dtos/FeaturedItemsDTO'
export { CollectionSummary } from './domain/models/CollectionSummary'
45 changes: 44 additions & 1 deletion src/collections/infra/repositories/CollectionsRepository.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,8 @@ import { ApiConstants } from '../../../core/infra/repositories/ApiConstants'
import { PublicationStatus } from '../../../core/domain/models/PublicationStatus'
import { ReadError } from '../../../core/domain/repositories/ReadError'
import { CollectionLinks } from '../../domain/models/CollectionLinks'
import { CollectionSummary } from '../../domain/models/CollectionSummary'
import { LinkingObjectType } from '../../domain/useCases/GetCollectionsForLinking'

export interface NewCollectionRequestPayload {
alias: string
Expand Down Expand Up @@ -93,7 +95,6 @@ export enum GetMyDataCollectionItemsQueryParams {

export class CollectionsRepository extends ApiRepository implements ICollectionsRepository {
private readonly collectionsResourceName: string = 'dataverses'

public async getCollection(
collectionIdOrAlias: number | string = ROOT_COLLECTION_ID
): Promise<Collection> {
Expand Down Expand Up @@ -485,4 +486,46 @@ export class CollectionsRepository extends ApiRepository implements ICollections
throw error
})
}

public async getCollectionsForLinking(
objectType: LinkingObjectType,
id: number | string,
searchTerm: string,
alreadyLinked: boolean
): Promise<CollectionSummary[]> {
let path: string
const queryParams = new URLSearchParams()
if (objectType === 'collection') {
path = `/${this.collectionsResourceName}/${id}/dataverse/linkingDataverses`
} else {
path = `/${this.collectionsResourceName}/:persistentId/dataset/linkingDataverses`
queryParams.set('persistentId', String(id))
}

if (searchTerm) {
queryParams.set('searchTerm', searchTerm)
}

if (alreadyLinked) {
queryParams.set('alreadyLinking', 'true')
}

return this.doGet(path, true, queryParams)
.then((response) => {
const payload = response.data.data as {
id: number
alias: string
name: string
}[]

return payload.map((item) => ({
id: item.id,
alias: item.alias,
displayName: item.name
}))
})
.catch((error) => {
throw error
})
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -159,7 +159,13 @@ export const transformCollectionLinksResponseToCollectionLinks = (
const responseDataPayload = response.data.data
const linkedCollections = responseDataPayload.linkedDataverses
const collectionsLinkingToThis = responseDataPayload.dataversesLinkingToThis
const linkedDatasets = responseDataPayload.linkedDatasets
const linkedDatasets = responseDataPayload.linkedDatasets.map(
(ld: { identifier: string; title: string }) => ({
persistentId: ld.identifier,
title: ld.title
})
)

return {
linkedCollections,
collectionsLinkingToThis,
Expand Down
4 changes: 2 additions & 2 deletions src/datasets/domain/repositories/IDatasetsRepository.ts
Original file line number Diff line number Diff line change
Expand Up @@ -65,8 +65,8 @@ export interface IDatasetsRepository {
): Promise<DatasetDownloadCount>
getDatasetVersionsSummaries(datasetId: number | string): Promise<DatasetVersionSummaryInfo[]>
deleteDatasetDraft(datasetId: number | string): Promise<void>
linkDataset(datasetId: number, collectionAlias: string): Promise<void>
unlinkDataset(datasetId: number, collectionAlias: string): Promise<void>
linkDataset(datasetId: number | string, collectionIdOrAlias: number | string): Promise<void>
unlinkDataset(datasetId: number | string, collectionIdOrAlias: number | string): Promise<void>
getDatasetLinkedCollections(datasetId: number | string): Promise<DatasetLinkedCollection[]>
getDatasetAvailableCategories(datasetId: number | string): Promise<string[]>
getDatasetCitationInOtherFormats(
Expand Down
8 changes: 4 additions & 4 deletions src/datasets/domain/useCases/LinkDataset.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,11 @@ export class LinkDataset implements UseCase<void> {
/**
* Creates a link between a Dataset and a Collection.
*
* @param {number} [datasetId] - The dataset id.
* @param {string} [collectionAlias] - The collection alias.
* @param {number | string} [datasetId] - The dataset id (numeric) or persistent identifier string.
* @param {number | string} [collectionIdOrAlias] - The collection identifier (numeric id) or alias.
* @returns {Promise<void>} - This method does not return anything upon successful completion.
*/
async execute(datasetId: number, collectionAlias: string): Promise<void> {
return await this.datasetsRepository.linkDataset(datasetId, collectionAlias)
async execute(datasetId: number | string, collectionIdOrAlias: number | string): Promise<void> {
return await this.datasetsRepository.linkDataset(datasetId, collectionIdOrAlias)
}
}
8 changes: 4 additions & 4 deletions src/datasets/domain/useCases/UnlinkDataset.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,11 @@ export class UnlinkDataset implements UseCase<void> {
/**
* Removes a link between a Dataset and a Collection.
*
* @param {number} [datasetId] - The dataset id.
* @param {string} [collectionAlias] - The collection alias.
* @param {number | string} [datasetId] - The dataset id (numeric) or persistent identifier string.
* @param {number | string} [collectionIdOrAlias] - The collection identifier (numeric id) or alias.
* @returns {Promise<void>} - This method does not return anything upon successful completion.
*/
async execute(datasetId: number, collectionAlias: string): Promise<void> {
return await this.datasetsRepository.unlinkDataset(datasetId, collectionAlias)
async execute(datasetId: number | string, collectionIdOrAlias: number | string): Promise<void> {
return await this.datasetsRepository.unlinkDataset(datasetId, collectionIdOrAlias)
}
}
24 changes: 20 additions & 4 deletions src/datasets/infra/repositories/DatasetsRepository.ts
Original file line number Diff line number Diff line change
Expand Up @@ -323,16 +323,32 @@ export class DatasetsRepository extends ApiRepository implements IDatasetsReposi
})
}

public async linkDataset(datasetId: number, collectionAlias: string): Promise<void> {
return this.doPut(`/${this.datasetsResourceName}/${datasetId}/link/${collectionAlias}`, {})
public async linkDataset(
datasetId: number | string,
collectionIdOrAlias: number | string
): Promise<void> {
const endpoint = this.buildApiEndpoint(
this.datasetsResourceName,
`link/${collectionIdOrAlias}`,
datasetId
)
return this.doPut(endpoint, {})
.then(() => undefined)
.catch((error) => {
throw error
})
}

public async unlinkDataset(datasetId: number, collectionAlias: string): Promise<void> {
return this.doDelete(`/${this.datasetsResourceName}/${datasetId}/deleteLink/${collectionAlias}`)
public async unlinkDataset(
datasetId: number | string,
collectionIdOrAlias: number | string
): Promise<void> {
const endpoint = this.buildApiEndpoint(
this.datasetsResourceName,
`deleteLink/${collectionIdOrAlias}`,
datasetId
)
return this.doDelete(endpoint)
.then(() => undefined)
.catch((error) => {
throw error
Expand Down
4 changes: 2 additions & 2 deletions test/environment/.env
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
POSTGRES_VERSION=17
DATAVERSE_DB_USER=dataverse
SOLR_VERSION=9.8.0
DATAVERSE_IMAGE_REGISTRY=docker.io
DATAVERSE_IMAGE_TAG=unstable
DATAVERSE_IMAGE_REGISTRY=ghcr.io
DATAVERSE_IMAGE_TAG=11710-find-dataverses-for-linking
DATAVERSE_BOOTSTRAP_TIMEOUT=5m
Loading