Skip to content
Merged
Show file tree
Hide file tree
Changes from 7 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
Binary file added .DS_Store
Comment thread
g-saracca marked this conversation as resolved.
Outdated
Binary file not shown.
8 changes: 8 additions & 0 deletions src/collections/domain/models/CollectionLinks.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import { CollectionSummary } from './CollectionSummary'
import { DatasetSummary } from '../../../datasets/domain/models/DatasetSummary'

export interface CollectionLinks {
linkedCollections: CollectionSummary[]
collectionsLinkingToThis: CollectionSummary[]
linkedDatasets: DatasetSummary[]
}
5 changes: 5 additions & 0 deletions src/collections/domain/models/CollectionSummary.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
export interface CollectionSummary {
Comment thread
g-saracca marked this conversation as resolved.
id: number
alias: string
displayName: string
}
10 changes: 10 additions & 0 deletions src/collections/domain/repositories/ICollectionsRepository.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import { CollectionSearchCriteria } from '../models/CollectionSearchCriteria'
import { CollectionUserPermissions } from '../models/CollectionUserPermissions'
import { PublicationStatus } from '../../../core/domain/models/PublicationStatus'
import { CollectionItemType } from '../../../collections/domain/models/CollectionItemType'
import { CollectionLinks } from '../models/CollectionLinks'

export interface ICollectionsRepository {
getCollection(collectionIdOrAlias: number | string): Promise<Collection>
Expand Down Expand Up @@ -50,4 +51,13 @@ export interface ICollectionsRepository {
): Promise<FeaturedItem[]>
deleteCollectionFeaturedItems(collectionIdOrAlias: number | string): Promise<void>
deleteCollectionFeaturedItem(featuredItemId: number): Promise<void>
linkCollection(
linkedCollectionIdOrAlias: number | string,
linkingCollectionIdOrAlias: number | string
): Promise<void>
unlinkCollection(
linkedCollectionIdOrAlias: number | string,
linkingCollectionIdOrAlias: number | string
): Promise<void>
getCollectionLinks(collectionIdOrAlias: number | string): Promise<CollectionLinks>
}
22 changes: 22 additions & 0 deletions src/collections/domain/useCases/GetCollectionLinks.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import { UseCase } from '../../../core/domain/useCases/UseCase'
import { ICollectionsRepository } from '../repositories/ICollectionsRepository'
import { CollectionLinks } from '../models/CollectionLinks'

export class GetCollectionItems implements UseCase<CollectionLinks> {
private collectionsRepository: ICollectionsRepository

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

/**
* Returns a CollectionLinks object containing other collections this collection is linked to, the other collections linking to this collection, and datasets linked to this collection, given the collection identifier or alias.
*
* @param {number | string} [collectionIdOrAlias] - A generic collection identifier, which can be either a string (for queries by CollectionAlias), or a number (for queries by CollectionId)
* If this parameter is not set, the default value is: ':root'
* @returns {Promise<CollectionLinks>}
*/
async execute(collectionId: number | string): Promise<CollectionLinks> {
return await this.collectionsRepository.getCollectionLinks(collectionId)
}
}
27 changes: 27 additions & 0 deletions src/collections/domain/useCases/LinkCollection.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import { UseCase } from '../../../core/domain/useCases/UseCase'
import { ICollectionsRepository } from '../repositories/ICollectionsRepository'

export class LinkCollection implements UseCase<void> {
private collectionsRepository: ICollectionsRepository

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

/**
* Deletes the Dataverse collection whose database ID or alias is given:
Comment thread
g-saracca marked this conversation as resolved.
Outdated
*
* @param {number| string} [linkedCollectionIdOrAlias] - The collection to be linked. Can be either a string (collection alias), or a number (collection id)
* @param { number | string} [linkingCollectionIdOrAlias] - The collection that will be linking to the linked collection. Can be either a string (collection alias), or a number (collection id)
* @returns {Promise<void>} -This method does not return anything upon successful completion.
*/
async execute(
linkedCollectionIdOrAlias: number | string,
linkingCollectionIdOrAlias: number | string
): Promise<void> {
return await this.collectionsRepository.linkCollection(
linkedCollectionIdOrAlias,
linkingCollectionIdOrAlias
)
}
}
27 changes: 27 additions & 0 deletions src/collections/domain/useCases/UnlinkCollection.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import { UseCase } from '../../../core/domain/useCases/UseCase'
import { ICollectionsRepository } from '../repositories/ICollectionsRepository'

export class UnlinkCollection implements UseCase<void> {
private collectionsRepository: ICollectionsRepository

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

/**
* Unlinks a collection from the collection that links to it
*
* @param {number| string} [linkedCollectionIdOrAlias] - The collection that is linked. Can be either a string (collection alias), or a number (collection id)
* @param { number | string} [linkingCollectionIdOrAlias] - The collection that links to the linked collection. Can be either a string (collection alias), or a number (collection id)
* @returns {Promise<void>} -This method does not return anything upon successful completion.
*/
async execute(
linkedCollectionIdOrAlias: number | string,
linkingCollectionIdOrAlias: number | string
): Promise<void> {
return await this.collectionsRepository.unlinkCollection(
linkedCollectionIdOrAlias,
linkingCollectionIdOrAlias
)
}
}
8 changes: 7 additions & 1 deletion src/collections/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ import { DeleteCollectionFeaturedItems } from './domain/useCases/DeleteCollectio
import { DeleteCollection } from './domain/useCases/DeleteCollection'
import { GetMyDataCollectionItems } from './domain/useCases/GetMyDataCollectionItems'
import { DeleteCollectionFeaturedItem } from './domain/useCases/DeleteCollectionFeaturedItem'
import { LinkCollection } from './domain/useCases/LinkCollection'
import { UnlinkCollection } from './domain/useCases/UnlinkCollection'

const collectionsRepository = new CollectionsRepository()

Expand All @@ -28,6 +30,8 @@ const updateCollectionFeaturedItems = new UpdateCollectionFeaturedItems(collecti
const deleteCollectionFeaturedItems = new DeleteCollectionFeaturedItems(collectionsRepository)
const deleteCollection = new DeleteCollection(collectionsRepository)
const deleteCollectionFeaturedItem = new DeleteCollectionFeaturedItem(collectionsRepository)
const linkCollection = new LinkCollection(collectionsRepository)
const unlinkCollection = new UnlinkCollection(collectionsRepository)

export {
getCollection,
Expand All @@ -42,7 +46,9 @@ export {
updateCollectionFeaturedItems,
deleteCollectionFeaturedItems,
deleteCollection,
deleteCollectionFeaturedItem
deleteCollectionFeaturedItem,
linkCollection,
unlinkCollection
Comment thread
g-saracca marked this conversation as resolved.
Outdated
}
export { Collection, CollectionInputLevel } from './domain/models/Collection'
export { CollectionFacet } from './domain/models/CollectionFacet'
Expand Down
36 changes: 36 additions & 0 deletions src/collections/infra/repositories/CollectionsRepository.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { ICollectionsRepository } from '../../domain/repositories/ICollectionsRe
import {
transformCollectionFacetsResponseToCollectionFacets,
transformCollectionItemsResponseToCollectionItemSubset,
transformCollectionLinksResponseToCollectionLinks,
transformCollectionResponseToCollection,
transformMyDataResponseToCollectionItemSubset
} from './transformers/collectionTransformers'
Expand Down Expand Up @@ -36,6 +37,7 @@ import {
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'

export interface NewCollectionRequestPayload {
alias: string
Expand Down Expand Up @@ -446,4 +448,38 @@ export class CollectionsRepository extends ApiRepository implements ICollections
throw error
})
}
public async linkCollection(
linkedCollectionIdOrAlias: number | string,
linkingCollectionIdOrAlias: number | string
): Promise<void> {
return this.doPut(
`/dataverses/${linkedCollectionIdOrAlias}` + `/link/${linkingCollectionIdOrAlias}`,
Comment thread
g-saracca marked this conversation as resolved.
Outdated
{} // No data is needed for this operation
)
.then(() => undefined)
.catch((error) => {
throw error
})
}
public async unlinkCollection(
linkedCollectionIdOrAlias: number | string,
linkingCollectionIdOrAlias: number | string
): Promise<void> {
return this.doDelete(
`/dataverses/${linkedCollectionIdOrAlias}` + `/deleteLink/${linkingCollectionIdOrAlias}`
Comment thread
g-saracca marked this conversation as resolved.
Outdated
)
.then(() => undefined)
.catch((error) => {
throw error
})
}
public async getCollectionLinks(collectionIdOrAlias: number | string): Promise<CollectionLinks> {
return this.doGet(`/${this.collectionsResourceName}/${collectionIdOrAlias}/links`, true)
.then((response) => {
return transformCollectionLinksResponseToCollectionLinks(response)
})
.catch((error) => {
throw error
})
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ import {
PublicationStatusCount
} from '../../../domain/models/MyDataCollectionItemSubset'
import { PublicationStatus } from '../../../../core/domain/models/PublicationStatus'
import { CollectionLinks } from '../../../domain/models/CollectionLinks'

export const transformCollectionResponseToCollection = (response: AxiosResponse): Collection => {
const collectionPayload = response.data.data
Expand Down Expand Up @@ -152,7 +153,19 @@ export const transformCollectionItemsResponseToCollectionItemSubset = (
...(countPerObjectType && { countPerObjectType })
}
}

export const transformCollectionLinksResponseToCollectionLinks = (
response: AxiosResponse
): CollectionLinks => {
const responseDataPayload = response.data.data
const linkedCollections = responseDataPayload.linkedDataverses
const collectionsLinkingToThis = responseDataPayload.dataversesLinkingToThis
const linkedDatasets = responseDataPayload.linkedDatasets
return {
linkedCollections,
collectionsLinkingToThis,
linkedDatasets
}
}
export const transformMyDataResponseToCollectionItemSubset = (
response: AxiosResponse
): MyDataCollectionItemSubset => {
Expand Down
4 changes: 4 additions & 0 deletions src/datasets/domain/models/DatasetSummary.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
export interface DatasetSummary {
persistentId: string
title: string
}
Binary file added test/.DS_Store
Comment thread
g-saracca marked this conversation as resolved.
Outdated
Binary file not shown.
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
Comment thread
g-saracca marked this conversation as resolved.
Outdated
DATAVERSE_IMAGE_TAG=11724-extend-list-dataverse-collection-links
DATAVERSE_BOOTSTRAP_TIMEOUT=5m
79 changes: 79 additions & 0 deletions test/functional/collections/LinkCollection.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
import {
ApiConfig,
WriteError,
createCollection,
getCollection,
linkCollection,
deleteCollection,
getCollectionItems
} from '../../../src'
import { TestConstants } from '../../testHelpers/TestConstants'
import { DataverseApiAuthMechanism } from '../../../src/core/infra/repositories/ApiConfig'
import { createCollectionDTO } from '../../testHelpers/collections/collectionHelper'

describe('execute', () => {
const firstCollectionAlias = 'linkCollection-functional-test-first'
const secondCollectionAlias = 'linkCollection-functional-test-second'

beforeEach(async () => {
ApiConfig.init(
TestConstants.TEST_API_URL,
DataverseApiAuthMechanism.API_KEY,
process.env.TEST_API_KEY
)
const firstCollection = createCollectionDTO(firstCollectionAlias)
const secondCollection = createCollectionDTO(secondCollectionAlias)
await createCollection.execute(firstCollection)
await createCollection.execute(secondCollection)
})

afterEach(async () => {
await Promise.all([
Comment thread
g-saracca marked this conversation as resolved.
getCollection
.execute(firstCollectionAlias)
.then((collection) =>
collection && collection.id ? deleteCollection.execute(collection.id) : null
),
getCollection
.execute(secondCollectionAlias)
.then((collection) =>
collection && collection.id ? deleteCollection.execute(collection.id) : null
)
])
})

test('should successfully link two collections', async () => {
const firstCollection = await getCollection.execute(firstCollectionAlias)
const secondCollection = await getCollection.execute(secondCollectionAlias)
expect.assertions(1)
try {
await linkCollection.execute(secondCollection.alias, firstCollection.alias)
} catch (error) {
throw new Error('Collections should be linked successfully')
} finally {
await new Promise((resolve) => setTimeout(resolve, 5000))
Comment thread
g-saracca marked this conversation as resolved.
const collectionItemSubset = await getCollectionItems.execute(firstCollectionAlias)

expect(collectionItemSubset.items.length).toBe(1)
}
})

test('should throw an error when linking a non-existent collection', async () => {
const invalidCollectionId = 99999
const firstCollection = await getCollection.execute(firstCollectionAlias)

expect.assertions(2)
let writeError: WriteError | undefined = undefined
try {
await linkCollection.execute(invalidCollectionId, firstCollection.id)
throw new Error('Use case should throw an error')
} catch (error) {
writeError = error as WriteError
} finally {
expect(writeError).toBeInstanceOf(WriteError)
expect(writeError?.message).toEqual(
`There was an error when writing the resource. Reason was: [404] Can't find dataverse with identifier='${invalidCollectionId}'`
)
}
})
})
Loading
Loading