Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
61c784f
feat: add GetMyDataCollectionItems use case
ekraffmiller Apr 24, 2025
4a80a4c
Merge branch 'develop' into 283-implement-my-data-use-case
ekraffmiller Apr 24, 2025
4b98fd4
feat: add tests
ekraffmiller Apr 24, 2025
722df10
fix: imports
ekraffmiller Apr 24, 2025
84259da
fix: unit test, remove logs
ekraffmiller Apr 25, 2025
4639b86
fix: functional test
ekraffmiller Apr 25, 2025
9e79bb4
change test data
ekraffmiller Apr 25, 2025
c57cc01
fix search query param
ekraffmiller Apr 25, 2025
edf0409
search for collection alias
ekraffmiller Apr 25, 2025
24ae72a
add more tests of response data
ekraffmiller Apr 25, 2025
02c8f6b
update documentation
ekraffmiller Apr 25, 2025
4a1f13e
fix documentation return value
ekraffmiller Apr 25, 2025
89dc408
fix parameter name to match API endpoint,
ekraffmiller Apr 28, 2025
34b2588
in countPerObjectType, change key to from 'dataverses' to 'collections'
ekraffmiller Apr 28, 2025
4519702
update countPerObjectType in tests
ekraffmiller Apr 29, 2025
4c62154
update countPerObjectType in unit tests
ekraffmiller Apr 29, 2025
35a85f3
revert wait time in PublishDataset.test.ts
ekraffmiller Apr 30, 2025
0a0dd88
use CollectionItemSubset.ts as return type
ekraffmiller Apr 30, 2025
a153656
add GetMyDataCollectionItems integration test
ekraffmiller Apr 30, 2025
7877358
fix import
ekraffmiller May 1, 2025
4a45184
fix integration tests
ekraffmiller May 1, 2025
5e53ed5
remove logs
ekraffmiller May 1, 2025
2042bd3
create new user for mydata integration test
ekraffmiller May 2, 2025
d7a1f4a
fix integration test
ekraffmiller May 2, 2025
d17df32
remove logging, make create superuser conditional
ekraffmiller May 2, 2025
af1dd72
remove commented code
ekraffmiller May 2, 2025
47d8743
fix use case description
ekraffmiller May 2, 2025
728df66
fix return type in documentation
ekraffmiller May 2, 2025
51208b7
remove console.log()
ekraffmiller May 5, 2025
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
57 changes: 57 additions & 0 deletions docs/useCases.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ The different use cases currently available in the package are classified below,
- [Get Collection Facets](#get-collection-facets)
- [Get User Permissions on a Collection](#get-user-permissions-on-a-collection)
- [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)
- [Collections write use cases](#collections-write-use-cases)
- [Create a Collection](#create-a-collection)
Expand Down Expand Up @@ -215,6 +216,62 @@ This use case supports the following optional parameters depending on the search
- **offset**: (number) Offset for pagination.
- **collectionSearchCriteria**: ([CollectionSearchCriteria](../src/collections/domain/models/CollectionSearchCriteria.ts)) Supports filtering the collection items by different properties.

#### List My Data Collection Items

Returns an instance of [CollectionItemSubset](../src/collections/domain/models/CollectionItemSubset.ts) that contains reduced information for each collection item for which the calling user has a role.

##### Example call:

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

/* ... */

const roleIds = [1, 2]
const collectionItemTypes = [CollectionItemType.DATASET, CollectionItemType.FILE]
const publishingStatuses = [
PublicationStatus.Published,
PublicationStatus.Draft,
PublicationStatus.Unpublished
]
const limit = 10
const selectedPage = 1
const searchText = 'search text'
const otherUserName = 'otherUserName'

getMyDataCollectionItems
.execute(
roleIds,
collectionItemTypes,
publishingStatuses,
limit,
selectedPage,
searchText,
otherUserName
)
.then((subset: CollectionItemSubset) => {
/* ... */
})

/* ... */
```

_See [use case](../src/collections/domain/useCases/GetMyDataCollectionItems.ts) implementation_.
This use case requires the following parameters:

- **roleIds** is an array of user role identifiers to filter the results. At least one roleId must be specified.
- **collectionItemTypes** is an array of collection item types to filter the results. At least one collectionItemType must be specified.
- **publishingStatuses** is an array of publishing statuses to filter the results. At least one publishingStatus must be specified.

This use case supports the following optional parameters depending on the search goals:

- **limit**: (number) Limit of items per page for pagination. (default is 10)
- **selectedPage**: (number) the page of results to be returned. (default is 1)
- **searchText** is an optional string to filter the results by.
- **otherUserName** is an optional string to return the collection items of another user. If not set, the calling user will be used. _Only superusers can use this parameter_.

The `CollectionItemSubset`returned instance contains a property called `totalItemCount` which is necessary for pagination.

#### Get Collection Featured Items

Returns a [CollectionFeaturedItem](../src/collections/domain/models/CollectionFeaturedItem.ts) array containing the featured items of the requested collection, given the collection identifier or alias.
Expand Down
4 changes: 2 additions & 2 deletions src/collections/domain/models/CollectionItemSubset.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,13 @@ export interface CollectionItemsFacet {
labels: CollectionItemsFacetLabel[]
}

interface CollectionItemsFacetLabel {
export interface CollectionItemsFacetLabel {
name: string
count: number
}

interface CountPerObjectType {
dataverses: number
collections: number
datasets: number
files: number
}
1 change: 1 addition & 0 deletions src/collections/domain/models/CollectionPreview.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,4 +12,5 @@ export interface CollectionPreview {
publicationStatuses: PublicationStatus[]
releaseOrCreateDate: Date
imageUrl?: string
userRoles?: string[]
}
11 changes: 11 additions & 0 deletions src/collections/domain/repositories/ICollectionsRepository.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ import { CollectionFeaturedItem } from '../models/CollectionFeaturedItem'
import { CollectionItemSubset } from '../models/CollectionItemSubset'
import { CollectionSearchCriteria } from '../models/CollectionSearchCriteria'
import { CollectionUserPermissions } from '../models/CollectionUserPermissions'
import { PublicationStatus } from '../../../../src/core/domain/models/PublicationStatus'
import { CollectionItemType } from '../../../../src/collections/domain/models/CollectionItemType'

export interface ICollectionsRepository {
getCollection(collectionIdOrAlias: number | string): Promise<Collection>
Expand All @@ -25,6 +27,15 @@ export interface ICollectionsRepository {
offset?: number,
collectionSearchCriteria?: CollectionSearchCriteria
): Promise<CollectionItemSubset>
getMyDataCollectionItems(
roleIds: number[],
collectionItemTypes: CollectionItemType[],
publicationStatuses: PublicationStatus[],
limit?: number,
selectedPage?: number,
searchText?: string,
otherUserName?: string
): Promise<CollectionItemSubset>
updateCollection(
collectionIdOrAlias: number | string,
updatedCollection: CollectionDTO
Expand Down
45 changes: 45 additions & 0 deletions src/collections/domain/useCases/GetMyDataCollectionItems.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import { UseCase } from '../../../core/domain/useCases/UseCase'
import { CollectionItemSubset } from '../models/CollectionItemSubset'
import { ICollectionsRepository } from '../repositories/ICollectionsRepository'
import { CollectionItemType } from '../../../../src/collections/domain/models/CollectionItemType'
import { PublicationStatus } from '../../../../src/core/domain/models/PublicationStatus'

export class GetMyDataCollectionItems implements UseCase<CollectionItemSubset> {
private collectionsRepository: ICollectionsRepository

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

/**
* Returns an instance of MyDataCollectionItemSubset that contains the items for which the user has the specified role or roles
*
* @param {number[]} [roleIds] - the ids of the roles to filter the items by.
* @param {CollectionItemType[]} [collectionItemTypes] - the types of items to filter by.
* @param {PublicationStatus[]} [publicationStatuses] - the publication statuses to filter by.
* @param {number} [limit] - Limit number of items to return for pagination (optional).
* @param {number} [selectedPage] - Offset (starting point) for pagination (optional).
* @param {string} [searchText] - filter by searching for this text in the results (optional).
* @param {string} [otherUserName] - filter by searching for this text in the results (optional).
* * @returns {Promise<CollectionItemSubset>}
*/
async execute(
roleIds: number[],
collectionItemTypes: CollectionItemType[],
publicationStatuses: PublicationStatus[],
limit?: number,
selectedPage?: number,
searchText?: string,
otherUserName?: string
): Promise<CollectionItemSubset> {
return this.collectionsRepository.getMyDataCollectionItems(
roleIds,
collectionItemTypes,
publicationStatuses,
limit,
selectedPage,
searchText,
otherUserName
)
}
}
3 changes: 3 additions & 0 deletions src/collections/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import { CollectionsRepository } from './infra/repositories/CollectionsRepositor
import { UpdateCollectionFeaturedItems } from './domain/useCases/UpdateCollectionFeaturedItems'
import { DeleteCollectionFeaturedItems } from './domain/useCases/DeleteCollectionFeaturedItems'
import { DeleteCollection } from './domain/useCases/DeleteCollection'
import { GetMyDataCollectionItems } from './domain/useCases/GetMyDataCollectionItems'

const collectionsRepository = new CollectionsRepository()

Expand All @@ -18,6 +19,7 @@ const createCollection = new CreateCollection(collectionsRepository)
const getCollectionFacets = new GetCollectionFacets(collectionsRepository)
const getCollectionUserPermissions = new GetCollectionUserPermissions(collectionsRepository)
const getCollectionItems = new GetCollectionItems(collectionsRepository)
const getMyDataCollectionItems = new GetMyDataCollectionItems(collectionsRepository)
const publishCollection = new PublishCollection(collectionsRepository)
const updateCollection = new UpdateCollection(collectionsRepository)
const getCollectionFeaturedItems = new GetCollectionFeaturedItems(collectionsRepository)
Expand All @@ -31,6 +33,7 @@ export {
getCollectionFacets,
getCollectionUserPermissions,
getCollectionItems,
getMyDataCollectionItems,
publishCollection,
updateCollection,
getCollectionFeaturedItems,
Expand Down
85 changes: 84 additions & 1 deletion src/collections/infra/repositories/CollectionsRepository.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@ import { ICollectionsRepository } from '../../domain/repositories/ICollectionsRe
import {
transformCollectionFacetsResponseToCollectionFacets,
transformCollectionItemsResponseToCollectionItemSubset,
transformCollectionResponseToCollection
transformCollectionResponseToCollection,
transformMyDataResponseToCollectionItemSubset
} from './transformers/collectionTransformers'
import { Collection, ROOT_COLLECTION_ID } from '../../domain/models/Collection'
import { CollectionDTO } from '../../domain/dtos/CollectionDTO'
Expand All @@ -21,6 +22,8 @@ import { CollectionFeaturedItem } from '../../domain/models/CollectionFeaturedIt
import { transformCollectionFeaturedItemsPayloadToCollectionFeaturedItems } from './transformers/collectionFeaturedItemsTransformer'
import { CollectionFeaturedItemsDTO } from '../../domain/dtos/CollectionFeaturedItemsDTO'
import { ApiConstants } from '../../../core/infra/repositories/ApiConstants'
import { PublicationStatus } from '../../../../src/core/domain/models/PublicationStatus'
import { ReadError } from '../../../core/domain/repositories/ReadError'

export interface NewCollectionRequestPayload {
alias: string
Expand Down Expand Up @@ -63,6 +66,16 @@ export enum GetCollectionItemsQueryParams {
SHOW_TYPE_COUNTS = 'show_type_counts'
}

export enum GetMyDataCollectionItemsQueryParams {
SEARCH_TEXT = 'mydata_search_term',
PER_PAGE = 'per_page',
SELECTED_PAGE = 'selected_page',
ROLE_ID = 'role_ids',
TYPE = 'dvobject_types',
PUBLISHED_STATES = 'published_states',
USER_IDENTIFIER = 'userIdentifier'
}

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

Expand Down Expand Up @@ -188,6 +201,76 @@ export class CollectionsRepository extends ApiRepository implements ICollections
})
}

public async getMyDataCollectionItems(
roleIds: number[],
collectionItemTypes: CollectionItemType[],
publicationStatuses: PublicationStatus[],
limit?: number,
selectedPage?: number,
searchText?: string,
userIdentifier?: string
): Promise<CollectionItemSubset> {
const queryParams = new URLSearchParams()

if (limit) {
queryParams.set(GetMyDataCollectionItemsQueryParams.PER_PAGE, limit.toString())
}

if (selectedPage) {
queryParams.set(GetMyDataCollectionItemsQueryParams.SELECTED_PAGE, selectedPage.toString())
}

if (searchText) {
queryParams.set(
GetMyDataCollectionItemsQueryParams.SEARCH_TEXT,
encodeURIComponent(searchText)
)
}

roleIds.forEach((roleId) => {
queryParams.append(GetMyDataCollectionItemsQueryParams.ROLE_ID, roleId.toString())
})
if (userIdentifier) {
queryParams.set(GetMyDataCollectionItemsQueryParams.USER_IDENTIFIER, userIdentifier)
}

collectionItemTypes.forEach((itemType) => {
let mappedItemType: string

switch (itemType) {
case CollectionItemType.COLLECTION:
mappedItemType = 'Dataverse'
break
case CollectionItemType.DATASET:
mappedItemType = 'Dataset'
break
case CollectionItemType.FILE:
mappedItemType = 'DataFile'
break
}
queryParams.append(GetMyDataCollectionItemsQueryParams.TYPE, mappedItemType)
})

publicationStatuses.forEach((publicationStatus) => {
queryParams.append(
GetMyDataCollectionItemsQueryParams.PUBLISHED_STATES,
publicationStatus.toString()
)
})
const result = await this.doGet('/mydata/retrieve', true, queryParams)
.then((response) => {
if (response.data.success !== true) {
throw new ReadError(response.data.error_message)
}
return transformMyDataResponseToCollectionItemSubset(response)
})
.catch((error) => {
throw error
})

return result
}

private createCreateOrUpdateRequestBody(
collectionDTO: CollectionDTO
): NewCollectionRequestPayload {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
export interface MyDataCollectionPreviewPayload {
name: string
parentDataverseName: string
identifier: string
parentDataverseIdentifier: string
url: string
image_url: string
description: string
type?: string
publicationStatuses: string[]
affiliation: string
published_at: string
user_roles: string[]
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
export interface MyDataCountPerObjectTypePayload {
dataverses_count: number
datasets_count: number
files_count: number
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
export interface MyDataPublicationStatusCountsPayload {
deaccessioned_count: number
draft_count: number
in_review_count: number
published_count: number
unpublished_count: number
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { PublicationStatus } from '../../../../core/domain/models/PublicationSta
import { CollectionItemType } from '../../../../collections/domain/models/CollectionItemType'
import { CollectionPreview } from '../../../domain/models/CollectionPreview'
import { CollectionPreviewPayload } from './CollectionPreviewPayload'
import { MyDataCollectionPreviewPayload } from './MyDataCollectionPreviewPayload'

export const transformCollectionPreviewPayloadToCollectionPreview = (
collectionPreviewPayload: CollectionPreviewPayload
Expand All @@ -25,3 +26,27 @@ export const transformCollectionPreviewPayloadToCollectionPreview = (
releaseOrCreateDate: new Date(collectionPreviewPayload.published_at)
}
}

export const transformMyDataCollectionPreviewPayloadToCollectionPreview = (
collectionPreviewPayload: MyDataCollectionPreviewPayload
): CollectionPreview => {
const publicationStatuses: PublicationStatus[] = []
collectionPreviewPayload.publicationStatuses.forEach((element) => {
publicationStatuses.push(element as unknown as PublicationStatus)
})
return {
type: CollectionItemType.COLLECTION,
name: collectionPreviewPayload.name,
parentName: collectionPreviewPayload.parentDataverseName,
alias: collectionPreviewPayload.identifier,
parentAlias: collectionPreviewPayload.parentDataverseIdentifier,
description: collectionPreviewPayload.description,
publicationStatuses: publicationStatuses,
affiliation: collectionPreviewPayload.affiliation,
...(collectionPreviewPayload.image_url && {
imageUrl: collectionPreviewPayload.image_url
}),
releaseOrCreateDate: new Date(collectionPreviewPayload.published_at),
userRoles: collectionPreviewPayload.user_roles
}
}
Loading