From 61c784f9cc4fe8669444bc95e467f114114d6845 Mon Sep 17 00:00:00 2001 From: Ellen Kraffmiller Date: Wed, 23 Apr 2025 21:28:58 -0400 Subject: [PATCH 01/28] feat: add GetMyDataCollectionItems use case --- .../domain/models/CollectionItemSubset.ts | 8 +- .../domain/models/CollectionPreview.ts | 1 + .../repositories/ICollectionsRepository.ts | 13 +- .../useCases/GetMyDataCollectionItems.ts | 43 +++++++ .../repositories/CollectionsRepository.ts | 81 ++++++++++++- .../MyDataCollectionPreviewPayload.ts | 14 +++ .../MyDataCountPerObjectTypePayload.ts | 5 + .../MyDataPublicationStatusCountsPayload.ts | 7 ++ .../collectionPreviewsTransformers.ts | 25 ++++ .../transformers/collectionTransformers.ts | 89 +++++++++++++- src/core/domain/models/PublicationStatus.ts | 4 +- src/datasets/domain/models/DatasetPreview.ts | 1 + .../MyDataDatasetPreviewPayload.ts | 21 ++++ .../datasetPreviewsTransformers.ts | 35 ++++++ src/files/domain/models/FilePreview.ts | 1 + .../transformers/MyDataFilePreviewPayload.ts | 90 ++++++++++++++ .../transformers/filePreviewTransformers.ts | 42 +++++++ .../GetCollectionItemsByUserRole.test.ts | 114 ++++++++++++++++++ 18 files changed, 585 insertions(+), 9 deletions(-) create mode 100644 src/collections/domain/useCases/GetMyDataCollectionItems.ts create mode 100644 src/collections/infra/repositories/transformers/MyDataCollectionPreviewPayload.ts create mode 100644 src/collections/infra/repositories/transformers/MyDataCountPerObjectTypePayload.ts create mode 100644 src/collections/infra/repositories/transformers/MyDataPublicationStatusCountsPayload.ts create mode 100644 src/datasets/infra/repositories/transformers/MyDataDatasetPreviewPayload.ts create mode 100644 src/files/infra/repositories/transformers/MyDataFilePreviewPayload.ts create mode 100644 test/unit/collections/GetCollectionItemsByUserRole.test.ts diff --git a/src/collections/domain/models/CollectionItemSubset.ts b/src/collections/domain/models/CollectionItemSubset.ts index 279b2e98..6775de4d 100644 --- a/src/collections/domain/models/CollectionItemSubset.ts +++ b/src/collections/domain/models/CollectionItemSubset.ts @@ -8,6 +8,12 @@ export interface CollectionItemSubset { totalItemCount: number countPerObjectType: CountPerObjectType } +export interface MyDataCollectionItemSubset { + items: (CollectionPreview | DatasetPreview | FilePreview)[] + publishingFacet: CollectionItemsFacetLabel[] + totalItemCount: number + countPerObjectType: CountPerObjectType +} export interface CollectionItemsFacet { name: string @@ -15,7 +21,7 @@ export interface CollectionItemsFacet { labels: CollectionItemsFacetLabel[] } -interface CollectionItemsFacetLabel { +export interface CollectionItemsFacetLabel { name: string count: number } diff --git a/src/collections/domain/models/CollectionPreview.ts b/src/collections/domain/models/CollectionPreview.ts index 85c0948a..3ec01646 100644 --- a/src/collections/domain/models/CollectionPreview.ts +++ b/src/collections/domain/models/CollectionPreview.ts @@ -12,4 +12,5 @@ export interface CollectionPreview { publicationStatuses: PublicationStatus[] releaseOrCreateDate: Date imageUrl?: string + userRoles?: string[] } diff --git a/src/collections/domain/repositories/ICollectionsRepository.ts b/src/collections/domain/repositories/ICollectionsRepository.ts index be784204..32de5c36 100644 --- a/src/collections/domain/repositories/ICollectionsRepository.ts +++ b/src/collections/domain/repositories/ICollectionsRepository.ts @@ -3,9 +3,11 @@ import { CollectionFeaturedItemsDTO } from '../dtos/CollectionFeaturedItemsDTO' import { Collection } from '../models/Collection' import { CollectionFacet } from '../models/CollectionFacet' import { CollectionFeaturedItem } from '../models/CollectionFeaturedItem' -import { CollectionItemSubset } from '../models/CollectionItemSubset' +import { CollectionItemSubset, MyDataCollectionItemSubset } from '../models/CollectionItemSubset' import { CollectionSearchCriteria } from '../models/CollectionSearchCriteria' import { CollectionUserPermissions } from '../models/CollectionUserPermissions' +import { CollectionItemType } from '../../../../dist' +import { PublicationStatus } from '../../../../dist/core/domain/models/PublicationStatus' export interface ICollectionsRepository { getCollection(collectionIdOrAlias: number | string): Promise @@ -25,6 +27,15 @@ export interface ICollectionsRepository { offset?: number, collectionSearchCriteria?: CollectionSearchCriteria ): Promise + getMyDataCollectionItems( + roleIds: number[], + collectionItemTypes: CollectionItemType[], + publicationStatuses: PublicationStatus[], + limit?: number, + page?: number, + searchText?: string, + otherUserName?: string + ): Promise updateCollection( collectionIdOrAlias: number | string, updatedCollection: CollectionDTO diff --git a/src/collections/domain/useCases/GetMyDataCollectionItems.ts b/src/collections/domain/useCases/GetMyDataCollectionItems.ts new file mode 100644 index 00000000..01bcd773 --- /dev/null +++ b/src/collections/domain/useCases/GetMyDataCollectionItems.ts @@ -0,0 +1,43 @@ +import { UseCase } from '../../../core/domain/useCases/UseCase' +import { MyDataCollectionItemSubset } from '../models/CollectionItemSubset' +import { ICollectionsRepository } from '../repositories/ICollectionsRepository' +import { CollectionItemType } from '../../../../dist' +import { PublicationStatus } from '../../../../dist/core/domain/models/PublicationStatus' + +export class GetMyDataCollectionItems implements UseCase { + 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 {number} [limit] - Limit for pagination (optional). + * @param {number} [offset] - Offset for pagination (optional). + * @param {string} [searchText] - filter by searching for this text in the results + * * @returns {Promise} + */ + async execute( + roleIds: number[], + collectionItemTypes: CollectionItemType[], + publicationStatuses: PublicationStatus[], + limit?: number, + offset?: number, + searchText?: string, + otherUserName?: string + ): Promise { + return await this.collectionsRepository.getMyDataCollectionItems( + roleIds, + collectionItemTypes, + publicationStatuses, + limit, + offset, + searchText, + otherUserName + ) + } +} diff --git a/src/collections/infra/repositories/CollectionsRepository.ts b/src/collections/infra/repositories/CollectionsRepository.ts index b3897f22..4e789128 100644 --- a/src/collections/infra/repositories/CollectionsRepository.ts +++ b/src/collections/infra/repositories/CollectionsRepository.ts @@ -3,14 +3,18 @@ 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' import { CollectionFacet } from '../../domain/models/CollectionFacet' import { CollectionUserPermissions } from '../../domain/models/CollectionUserPermissions' import { transformCollectionUserPermissionsResponseToCollectionUserPermissions } from './transformers/collectionUserPermissionsTransformers' -import { CollectionItemSubset } from '../../domain/models/CollectionItemSubset' +import { + CollectionItemSubset, + MyDataCollectionItemSubset +} from '../../domain/models/CollectionItemSubset' import { CollectionSearchCriteria, OrderType, @@ -21,6 +25,7 @@ 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 '../../../../dist/core/domain/models/PublicationStatus' export interface NewCollectionRequestPayload { alias: string @@ -63,6 +68,17 @@ export enum GetCollectionItemsQueryParams { SHOW_TYPE_COUNTS = 'show_type_counts' } +export enum GetMyDataCollectionItemsQueryParams { + QUERY = 'q', + PER_PAGE = 'per_page', + SELECTED_PAGE = 'selected_page', + ROLE_ID = 'role_ids', + TYPE = 'dvobject_types', + PUBLISHED_STATES = 'published_states', + USER_IDENTIFIER = 'userIdentifier', + SHOW_TYPE_COUNTS = 'show_type_counts' +} + export class CollectionsRepository extends ApiRepository implements ICollectionsRepository { private readonly collectionsResourceName: string = 'dataverses' @@ -188,6 +204,67 @@ export class CollectionsRepository extends ApiRepository implements ICollections }) } + public async getMyDataCollectionItems( + roleIds: number[], + collectionItemTypes: CollectionItemType[], + publicationStatuses: PublicationStatus[], + limit?: number, + page?: number, + searchText?: string, + userIdentifier?: string + ): Promise { + const queryParams = new URLSearchParams() + + if (limit) { + queryParams.set(GetMyDataCollectionItemsQueryParams.PER_PAGE, limit.toString()) + } + + if (page) { + queryParams.set(GetMyDataCollectionItemsQueryParams.SELECTED_PAGE, page.toString()) + } + + if (searchText) { + queryParams.set(GetMyDataCollectionItemsQueryParams.QUERY, 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() + ) + }) + + return this.doGet('/retrieve', true, queryParams) + .then((response) => transformMyDataResponseToCollectionItemSubset(response)) + .catch((error) => { + throw error + }) + } + private createCreateOrUpdateRequestBody( collectionDTO: CollectionDTO ): NewCollectionRequestPayload { diff --git a/src/collections/infra/repositories/transformers/MyDataCollectionPreviewPayload.ts b/src/collections/infra/repositories/transformers/MyDataCollectionPreviewPayload.ts new file mode 100644 index 00000000..bc0d4b6c --- /dev/null +++ b/src/collections/infra/repositories/transformers/MyDataCollectionPreviewPayload.ts @@ -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[] +} diff --git a/src/collections/infra/repositories/transformers/MyDataCountPerObjectTypePayload.ts b/src/collections/infra/repositories/transformers/MyDataCountPerObjectTypePayload.ts new file mode 100644 index 00000000..fb27d4de --- /dev/null +++ b/src/collections/infra/repositories/transformers/MyDataCountPerObjectTypePayload.ts @@ -0,0 +1,5 @@ +export interface MyDataCountPerObjectTypePayload { + dataverses_count: number + datasets_count: number + files_count: number +} diff --git a/src/collections/infra/repositories/transformers/MyDataPublicationStatusCountsPayload.ts b/src/collections/infra/repositories/transformers/MyDataPublicationStatusCountsPayload.ts new file mode 100644 index 00000000..ea9d989b --- /dev/null +++ b/src/collections/infra/repositories/transformers/MyDataPublicationStatusCountsPayload.ts @@ -0,0 +1,7 @@ +export interface MyDataPublicationStatusCountsPayload { + deaccessioned_count: number + draft_count: number + in_review_count: number + published_count: number + unpublished_count: number +} diff --git a/src/collections/infra/repositories/transformers/collectionPreviewsTransformers.ts b/src/collections/infra/repositories/transformers/collectionPreviewsTransformers.ts index bbbf1af1..25c21c6c 100644 --- a/src/collections/infra/repositories/transformers/collectionPreviewsTransformers.ts +++ b/src/collections/infra/repositories/transformers/collectionPreviewsTransformers.ts @@ -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 @@ -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 + } +} diff --git a/src/collections/infra/repositories/transformers/collectionTransformers.ts b/src/collections/infra/repositories/transformers/collectionTransformers.ts index 33c6e9b7..c3523cea 100644 --- a/src/collections/infra/repositories/transformers/collectionTransformers.ts +++ b/src/collections/infra/repositories/transformers/collectionTransformers.ts @@ -10,21 +10,37 @@ import { CollectionFacet } from '../../../domain/models/CollectionFacet' import { CollectionFacetPayload } from './CollectionFacetPayload' import { CollectionItemsFacet, - CollectionItemSubset + CollectionItemsFacetLabel, + CollectionItemSubset, + MyDataCollectionItemSubset } from '../../../domain/models/CollectionItemSubset' import { DatasetPreview } from '../../../../datasets' import { FilePreview } from '../../../../files' import { DatasetPreviewPayload } from '../../../../datasets/infra/repositories/transformers/DatasetPreviewPayload' import { FilePreviewPayload } from '../../../../files/infra/repositories/transformers/FilePreviewPayload' -import { transformDatasetPreviewPayloadToDatasetPreview } from '../../../../datasets/infra/repositories/transformers/datasetPreviewsTransformers' -import { transformFilePreviewPayloadToFilePreview } from '../../../../files/infra/repositories/transformers/filePreviewTransformers' -import { transformCollectionPreviewPayloadToCollectionPreview } from './collectionPreviewsTransformers' +import { + transformDatasetPreviewPayloadToDatasetPreview, + transformMyDataDatasetPreviewPayloadToDatasetPreview +} from '../../../../datasets/infra/repositories/transformers/datasetPreviewsTransformers' +import { + transformFilePreviewPayloadToFilePreview, + transformMyDataFilePreviewPayloadToFilePreview +} from '../../../../files/infra/repositories/transformers/filePreviewTransformers' +import { + transformCollectionPreviewPayloadToCollectionPreview, + transformMyDataCollectionPreviewPayloadToCollectionPreview +} from './collectionPreviewsTransformers' import { CollectionPreviewPayload } from './CollectionPreviewPayload' import { CollectionPreview } from '../../../domain/models/CollectionPreview' import { CollectionContact } from '../../../domain/models/CollectionContact' import { CollectionType } from '../../../domain/models/CollectionType' import { CollectionItemsFacetPayload } from './CollectionItemsFacetsPayload' import { CollectionItemsCountPerObjectTypePayload } from './CollectionItemsCountPerObjectTypePayload' +import { MyDataFilePreviewPayload } from '../../../../files/infra/repositories/transformers/MyDataFilePreviewPayload' +import { MyDataDatasetPreviewPayload } from '../../../../datasets/infra/repositories/transformers/MyDataDatasetPreviewPayload' +import { MyDataCollectionPreviewPayload } from './MyDataCollectionPreviewPayload' +import { MyDataCountPerObjectTypePayload } from './MyDataCountPerObjectTypePayload' +import { MyDataPublicationStatusCountsPayload } from './MyDataPublicationStatusCountsPayload' export const transformCollectionResponseToCollection = (response: AxiosResponse): Collection => { const collectionPayload = response.data.data @@ -132,6 +148,59 @@ export const transformCollectionItemsResponseToCollectionItemSubset = ( } } +export const transformMyDataResponseToCollectionItemSubset = ( + response: AxiosResponse +): MyDataCollectionItemSubset => { + const responseDataPayload = response.data.data + const itemsPayload = responseDataPayload.items + const countPerObjectTypePayload = responseDataPayload[ + 'dvobject_counts' + ] as MyDataCountPerObjectTypePayload + + const items: (DatasetPreview | FilePreview | CollectionPreview)[] = [] + + itemsPayload.forEach(function ( + itemPayload: + | MyDataCollectionPreviewPayload + | MyDataDatasetPreviewPayload + | MyDataFilePreviewPayload + ) { + if (itemPayload.type === 'file') { + items.push( + transformMyDataFilePreviewPayloadToFilePreview(itemPayload as MyDataFilePreviewPayload) + ) + } else if (itemPayload.type === 'dataset') { + items.push( + transformMyDataDatasetPreviewPayloadToDatasetPreview( + itemPayload as MyDataDatasetPreviewPayload + ) + ) + } else if (itemPayload.type === 'dataverse') { + items.push( + transformMyDataCollectionPreviewPayloadToCollectionPreview( + itemPayload as unknown as MyDataCollectionPreviewPayload + ) + ) + } + }) + + const countPerObjectType = { + dataverses: countPerObjectTypePayload['dataverses_count'], + datasets: countPerObjectTypePayload['datasets_count'], + files: countPerObjectTypePayload['files_count'] + } + const publishingFacet: CollectionItemsFacetLabel[] = transformPublicationStatusResponseToLabels( + responseDataPayload.pubstatus_counts as MyDataPublicationStatusCountsPayload + ) + + return { + items, + publishingFacet, + totalItemCount: responseDataPayload.pagination.numResults, + countPerObjectType + } +} + const transformContactsPayloadToContacts = ( contactsPayload: CollectionContactPayload[] ): CollectionContact[] => { @@ -140,3 +209,15 @@ const transformContactsPayloadToContacts = ( displayOrder: contactPayload.displayOrder })) } + +const transformPublicationStatusResponseToLabels = ( + publicationStatusCounts: MyDataPublicationStatusCountsPayload +): CollectionItemsFacetLabel[] => { + const labels: CollectionItemsFacetLabel[] = [] + labels.push({ name: 'Published', count: publicationStatusCounts.published_count }) + labels.push({ name: 'Unpublished', count: publicationStatusCounts.unpublished_count }) + labels.push({ name: 'Draft ', count: publicationStatusCounts.draft_count }) + labels.push({ name: 'In Review', count: publicationStatusCounts.in_review_count }) + labels.push({ name: 'Deaccessioned', count: publicationStatusCounts.deaccessioned_count }) + return labels +} diff --git a/src/core/domain/models/PublicationStatus.ts b/src/core/domain/models/PublicationStatus.ts index a56386f3..d2266979 100644 --- a/src/core/domain/models/PublicationStatus.ts +++ b/src/core/domain/models/PublicationStatus.ts @@ -1,5 +1,7 @@ export enum PublicationStatus { Published = 'Published', Unpublished = 'Unpublished', - Draft = 'Draft' + Draft = 'Draft', + Deaccessioned = 'Deaccessioned', + InReview = 'In Review' } diff --git a/src/datasets/domain/models/DatasetPreview.ts b/src/datasets/domain/models/DatasetPreview.ts index f139ec0d..5ac1da00 100644 --- a/src/datasets/domain/models/DatasetPreview.ts +++ b/src/datasets/domain/models/DatasetPreview.ts @@ -14,4 +14,5 @@ export interface DatasetPreview { parentCollectionName: string parentCollectionAlias: string imageUrl?: string + userRoles?: string[] } diff --git a/src/datasets/infra/repositories/transformers/MyDataDatasetPreviewPayload.ts b/src/datasets/infra/repositories/transformers/MyDataDatasetPreviewPayload.ts new file mode 100644 index 00000000..6b11529d --- /dev/null +++ b/src/datasets/infra/repositories/transformers/MyDataDatasetPreviewPayload.ts @@ -0,0 +1,21 @@ +export interface MyDataDatasetPreviewPayload { + name: string + type: string + global_id: string + description: string + publisher: string + citationHtml: string + identifier_of_dataverse: string + name_of_dataverse: string + publicationStatuses: string[] + majorVersion: number + minorVersion: number + versionId: number + versionState: string + createdAt: string + updatedAt: string + publication_statuses: string[] + user_roles: string[] + image_url?: string + published_at?: string +} diff --git a/src/datasets/infra/repositories/transformers/datasetPreviewsTransformers.ts b/src/datasets/infra/repositories/transformers/datasetPreviewsTransformers.ts index 2f87ac19..26d59c66 100644 --- a/src/datasets/infra/repositories/transformers/datasetPreviewsTransformers.ts +++ b/src/datasets/infra/repositories/transformers/datasetPreviewsTransformers.ts @@ -5,6 +5,7 @@ import { DatasetPreviewSubset } from '../../../domain/models/DatasetPreviewSubse import { DatasetPreviewPayload } from './DatasetPreviewPayload' import { PublicationStatus } from '../../../../core/domain/models/PublicationStatus' import { CollectionItemType } from '../../../../collections/domain/models/CollectionItemType' +import { MyDataDatasetPreviewPayload } from './MyDataDatasetPreviewPayload' export const transformDatasetPreviewsResponseToDatasetPreviewSubset = ( response: AxiosResponse @@ -53,3 +54,37 @@ export const transformDatasetPreviewPayloadToDatasetPreview = ( }) } } + +export const transformMyDataDatasetPreviewPayloadToDatasetPreview = ( + datasetPreviewPayload: MyDataDatasetPreviewPayload +): DatasetPreview => { + const publicationStatuses: PublicationStatus[] = [] + datasetPreviewPayload.publicationStatuses.forEach((element) => { + publicationStatuses.push(element as unknown as PublicationStatus) + }) + return { + type: CollectionItemType.DATASET, + persistentId: datasetPreviewPayload.global_id, + title: datasetPreviewPayload.name, + versionId: datasetPreviewPayload.versionId, + versionInfo: { + majorNumber: datasetPreviewPayload.majorVersion, + minorNumber: datasetPreviewPayload.minorVersion, + state: datasetPreviewPayload.versionState as DatasetVersionState, + createTime: new Date(datasetPreviewPayload.createdAt), + lastUpdateTime: new Date(datasetPreviewPayload.updatedAt), + ...(datasetPreviewPayload.published_at && { + releaseTime: new Date(datasetPreviewPayload.published_at) + }) + }, + citation: datasetPreviewPayload.citationHtml, + description: datasetPreviewPayload.description, + publicationStatuses: publicationStatuses, + parentCollectionAlias: datasetPreviewPayload.identifier_of_dataverse, + parentCollectionName: datasetPreviewPayload.name_of_dataverse, + ...(datasetPreviewPayload.image_url && { + imageUrl: datasetPreviewPayload.image_url + }), + userRoles: datasetPreviewPayload.user_roles + } +} diff --git a/src/files/domain/models/FilePreview.ts b/src/files/domain/models/FilePreview.ts index 5179b407..ab74f1d5 100644 --- a/src/files/domain/models/FilePreview.ts +++ b/src/files/domain/models/FilePreview.ts @@ -27,6 +27,7 @@ export interface FilePreview { tabularTags?: string[] variables?: number observations?: number + userRoles?: string[] } export interface FilePreviewChecksum { diff --git a/src/files/infra/repositories/transformers/MyDataFilePreviewPayload.ts b/src/files/infra/repositories/transformers/MyDataFilePreviewPayload.ts new file mode 100644 index 00000000..a8a28fa4 --- /dev/null +++ b/src/files/infra/repositories/transformers/MyDataFilePreviewPayload.ts @@ -0,0 +1,90 @@ +import { FilePreviewChecksumPayload } from '../../../../../dist/files/infra/repositories/transformers/FilePreviewPayload' + +export interface MyDataFilePreviewPayload { + name: string + type: string + url: string + file_id: string + file_type: string + file_content_type: string + size_in_bytes: number + md5: string + checksum: FilePreviewChecksumPayload + unf: string + dataset_name: string + dataset_id: string + dataset_persistent_id: string + dataset_citation: string + restricted: boolean + canDownloadFile: boolean + matches: string[] + score: number + entity_id: number + publicationStatuses: string[] + releaseOrCreateDate: string + is_draft_state: boolean + is_in_review_state: boolean + is_unpublished_state: boolean + is_published: boolean + is_deaccesioned: boolean + is_valid: boolean + date_to_display_on_card: string + parentIdentifier: string + parentName: string + user_roles: string[] + image_url?: string + variables?: number + observations?: number + file_persistent_id?: string + description?: string +} +/* +{ + "name": "teacher_survey.tab", + "type": "file", + "url": "http://localhost:8080/api/access/datafile/15", + "file_id": "15", + "file_type": "Comma Separated Values", + "file_content_type": "text/comma-separated-values", + "size_in_bytes": 174644, + "md5": "1db96dd4229c5bf5e6d8b1e6a301dca0", + "checksum": { + "type": "MD5", + "value": "1db96dd4229c5bf5e6d8b1e6a301dca0" + }, + "unf": "UNF:6:2Q0xnmkmgp6vi/lAa+bhRQ==", + "dataset_name": "Never Published", + "dataset_id": "14", + "dataset_persistent_id": "doi:10.5072/FK2/IHYE1J", + "dataset_citation": "Admin, Dataverse, 2025, \"Never Published\", https://doi.org/10.5072/FK2/IHYE1J, Root, DRAFT VERSION, UNF:6:2Q0xnmkmgp6vi/lAa+bhRQ== [fileUNF]", + "restricted": false, + "variables": 10, + "observations": 7000, + "canDownloadFile": true, + "matches": [], + "score": 321.1235656738281, + "entity_id": 15, + "publicationStatuses": [ + "Unpublished", + "Draft" + ], + "releaseOrCreateDate": "2025-04-23T13:48:15Z", + "publication_statuses": [ + "Unpublished", + "Draft" + ], + "is_draft_state": true, + "is_in_review_state": false, + "is_unpublished_state": true, + "is_published": false, + "is_deaccesioned": false, + "is_valid": true, + "date_to_display_on_card": "Apr 23, 2025", + "parentIdentifier": "doi:10.5072/FK2/IHYE1J", + "parentName": "Never Published", + "user_roles": [ + "Admin", + "Contributor" + ] +} +*/ diff --git a/src/files/infra/repositories/transformers/filePreviewTransformers.ts b/src/files/infra/repositories/transformers/filePreviewTransformers.ts index 0c91ce30..514e58da 100644 --- a/src/files/infra/repositories/transformers/filePreviewTransformers.ts +++ b/src/files/infra/repositories/transformers/filePreviewTransformers.ts @@ -2,6 +2,7 @@ import { CollectionItemType } from '../../../../collections/domain/models/Collec import { PublicationStatus } from '../../../../core/domain/models/PublicationStatus' import { FilePreview } from '../../../domain/models/FilePreview' import { FilePreviewPayload } from './FilePreviewPayload' +import { MyDataFilePreviewPayload } from './MyDataFilePreviewPayload' export const transformFilePreviewPayloadToFilePreview = ( filePreviewPayload: FilePreviewPayload @@ -47,3 +48,44 @@ export const transformFilePreviewPayloadToFilePreview = ( ...(filePreviewPayload.observations && { observations: filePreviewPayload.observations }) } } +export const transformMyDataFilePreviewPayloadToFilePreview = ( + filePreviewPayload: MyDataFilePreviewPayload +): FilePreview => { + const publicationStatuses: PublicationStatus[] = [] + filePreviewPayload.publicationStatuses.forEach((element) => { + publicationStatuses.push(element as unknown as PublicationStatus) + }) + return { + type: CollectionItemType.FILE, + name: filePreviewPayload.name, + url: filePreviewPayload.url, + ...(filePreviewPayload.image_url && { imageUrl: filePreviewPayload.image_url }), + fileId: Number(filePreviewPayload.file_id), + ...(filePreviewPayload.file_persistent_id && { + filePersistentId: filePreviewPayload.file_persistent_id + }), + description: filePreviewPayload.description ?? '', + fileType: filePreviewPayload.file_type, + fileContentType: filePreviewPayload.file_content_type, + sizeInBytes: filePreviewPayload.size_in_bytes, + ...(filePreviewPayload.md5 && { md5: filePreviewPayload.md5 }), + ...(filePreviewPayload.checksum && { + checksum: { + type: filePreviewPayload.checksum.type, + value: filePreviewPayload.checksum.value + } + }), + unf: filePreviewPayload.unf, + datasetName: filePreviewPayload.dataset_name, + datasetId: Number(filePreviewPayload.dataset_id), + datasetPersistentId: filePreviewPayload.dataset_persistent_id, + datasetCitation: filePreviewPayload.dataset_citation, + publicationStatuses: publicationStatuses, + releaseOrCreateDate: new Date(filePreviewPayload.releaseOrCreateDate), + restricted: filePreviewPayload.restricted, + canDownloadFile: filePreviewPayload.canDownloadFile, + ...(filePreviewPayload.variables && { variables: filePreviewPayload.variables }), + ...(filePreviewPayload.observations && { observations: filePreviewPayload.observations }), + userRoles: filePreviewPayload.user_roles + } +} diff --git a/test/unit/collections/GetCollectionItemsByUserRole.test.ts b/test/unit/collections/GetCollectionItemsByUserRole.test.ts new file mode 100644 index 00000000..f300d175 --- /dev/null +++ b/test/unit/collections/GetCollectionItemsByUserRole.test.ts @@ -0,0 +1,114 @@ +import { ICollectionsRepository } from '../../../src/collections/domain/repositories/ICollectionsRepository' +import { GetMyDataCollectionItems } from '../../../src/collections/domain/useCases/GetMyDataCollectionItems' +import { ReadError } from '../../../src' +import { CollectionItemType } from '../../../dist' +import { createDatasetPreviewModel } from '../../testHelpers/datasets/datasetPreviewHelper' +import { createFilePreviewModel } from '../../testHelpers/files/filePreviewHelper' +import { createCollectionPreviewModel } from '../../testHelpers/collections/collectionPreviewHelper' +import { MyDataCollectionItemSubset } from '../../../src/collections/domain/models/CollectionItemSubset' + +describe('GetCollectionItemsByUserRole', () => { + let collectionRepositoryStub: ICollectionsRepository + let testGetMyDataCollectionItems: GetMyDataCollectionItems + + const testRoleIds = [1, 2] + const testCollectionItemTypes = [CollectionItemType.DATASET, CollectionItemType.FILE] + const testLimit = 10 + const testPage = 1 + const testSearchText = 'test' + const testItems = [ + createDatasetPreviewModel(), + createFilePreviewModel(), + createCollectionPreviewModel() + ] + + const testItemSubset: MyDataCollectionItemSubset = { + items: testItems, + publishingFacet: testFacets, + totalItemCount: testTotalCount, + countPerObjectType: testCountPerObjectType + } + beforeEach(() => { + collectionRepositoryStub = {} as ICollectionsRepository + testGetMyDataCollectionItems = new GetMyDataCollectionItems(collectionRepositoryStub) + }) + + test('should return item subset on repository success', async () => { + collectionRepositoryStub.getMyDataCollectionItems = jest.fn().mockResolvedValue(testItemSubset) + + const actual = await testGetMyDataCollectionItems.execute( + testRoleIds, + testCollectionItemTypes, + testLimit, + testPage, + testSearchText + ) + + expect(actual).toEqual(testItemSubset) + }) + + test('should return error result on repository error', async () => { + collectionRepositoryStub.getMyDataCollectionItems = jest.fn().mockRejectedValue(new ReadError()) + + await expect( + testGetMyDataCollectionItems.execute( + testRoleIds, + testCollectionItemTypes, + testLimit, + testPage, + testSearchText + ) + ).rejects.toThrow(ReadError) + }) + + test('should handle roleIds parameter', async () => { + collectionRepositoryStub.getMyDataCollectionItems = jest.fn().mockResolvedValue(testItemSubset) + + const actual = await testGetMyDataCollectionItems.execute(testRoleIds, [], undefined) + + expect(collectionRepositoryStub.getMyDataCollectionItems).toHaveBeenCalledWith( + testRoleIds, + [], + undefined, + undefined, + undefined + ) + expect(actual).toEqual(testItemSubset) + }) + + test('should handle collectionItemTypes parameter', async () => { + collectionRepositoryStub.getMyDataCollectionItems = jest.fn().mockResolvedValue(testItemSubset) + + const actual = await testGetMyDataCollectionItems.execute([], testCollectionItemTypes) + + expect(collectionRepositoryStub.getMyDataCollectionItems).toHaveBeenCalledWith( + [], + testCollectionItemTypes, + undefined, + undefined, + undefined + ) + expect(actual).toEqual(testItemSubset) + }) + + test('should handle all parameters', async () => { + collectionRepositoryStub.getMyDataCollectionItems = jest.fn().mockResolvedValue(testItemSubset) + + const actual = await testGetMyDataCollectionItems.execute( + testRoleIds, + testCollectionItemTypes, + testLimit, + testPage, + testSearchText + ) + + expect(collectionRepositoryStub.getMyDataCollectionItems).toHaveBeenCalledWith( + testRoleIds, + testCollectionItemTypes, + testLimit, + testPage, + testSearchText + ) + expect(actual).toEqual(testItemSubset) + }) +}) From 4b98fd4f612095a3b67328831700265a086d2364 Mon Sep 17 00:00:00 2001 From: Ellen Kraffmiller Date: Thu, 24 Apr 2025 16:37:08 -0400 Subject: [PATCH 02/28] feat: add tests --- src/collections/index.ts | 3 + .../repositories/CollectionsRepository.ts | 12 +- .../transformers/collectionTransformers.ts | 3 +- .../GetMyDataCollectionItems.test.ts | 177 ++++++++++++++++++ ...st.ts => GetMyDataCollectionItems.test.ts} | 65 ++++--- 5 files changed, 234 insertions(+), 26 deletions(-) create mode 100644 test/functional/collections/GetMyDataCollectionItems.test.ts rename test/unit/collections/{GetCollectionItemsByUserRole.test.ts => GetMyDataCollectionItems.test.ts} (72%) diff --git a/src/collections/index.ts b/src/collections/index.ts index f65f121a..07e79ad3 100644 --- a/src/collections/index.ts +++ b/src/collections/index.ts @@ -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() @@ -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) @@ -31,6 +33,7 @@ export { getCollectionFacets, getCollectionUserPermissions, getCollectionItems, + getMyDataCollectionItems, publishCollection, updateCollection, getCollectionFeaturedItems, diff --git a/src/collections/infra/repositories/CollectionsRepository.ts b/src/collections/infra/repositories/CollectionsRepository.ts index 4e789128..2df25090 100644 --- a/src/collections/infra/repositories/CollectionsRepository.ts +++ b/src/collections/infra/repositories/CollectionsRepository.ts @@ -26,6 +26,7 @@ import { transformCollectionFeaturedItemsPayloadToCollectionFeaturedItems } from import { CollectionFeaturedItemsDTO } from '../../domain/dtos/CollectionFeaturedItemsDTO' import { ApiConstants } from '../../../core/infra/repositories/ApiConstants' import { PublicationStatus } from '../../../../dist/core/domain/models/PublicationStatus' +import { ReadError } from '../../../core/domain/repositories/ReadError' export interface NewCollectionRequestPayload { alias: string @@ -258,9 +259,16 @@ export class CollectionsRepository extends ApiRepository implements ICollections ) }) - return this.doGet('/retrieve', true, queryParams) - .then((response) => transformMyDataResponseToCollectionItemSubset(response)) + return 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) => { + console.error('Error in getMyDataCollectionItems:', error) + console.log('Error response:', error.response) throw error }) } diff --git a/src/collections/infra/repositories/transformers/collectionTransformers.ts b/src/collections/infra/repositories/transformers/collectionTransformers.ts index c3523cea..cbc920fe 100644 --- a/src/collections/infra/repositories/transformers/collectionTransformers.ts +++ b/src/collections/infra/repositories/transformers/collectionTransformers.ts @@ -151,8 +151,9 @@ export const transformCollectionItemsResponseToCollectionItemSubset = ( export const transformMyDataResponseToCollectionItemSubset = ( response: AxiosResponse ): MyDataCollectionItemSubset => { + console.log('response', JSON.stringify(response.data)) const responseDataPayload = response.data.data - const itemsPayload = responseDataPayload.items + const itemsPayload = responseDataPayload.items ?? [] const countPerObjectTypePayload = responseDataPayload[ 'dvobject_counts' ] as MyDataCountPerObjectTypePayload diff --git a/test/functional/collections/GetMyDataCollectionItems.test.ts b/test/functional/collections/GetMyDataCollectionItems.test.ts new file mode 100644 index 00000000..ff948992 --- /dev/null +++ b/test/functional/collections/GetMyDataCollectionItems.test.ts @@ -0,0 +1,177 @@ +import { + ApiConfig, + CreatedDatasetIdentifiers, + DatasetPreview, + FilePreview, + ReadError, + createDataset, + getMyDataCollectionItems +} from '../../../src' +import { TestConstants } from '../../testHelpers/TestConstants' +import { DataverseApiAuthMechanism } from '../../../src/core/infra/repositories/ApiConfig' +import { + createCollectionViaApi, + deleteCollectionViaApi +} from '../../testHelpers/collections/collectionHelper' +import { uploadFileViaApi } from '../../testHelpers/files/filesHelper' +import { deleteUnpublishedDatasetViaApi } from '../../testHelpers/datasets/datasetHelper' +import { CollectionItemType } from '../../../dist' +import { PublicationStatus } from '../../../dist/core/domain/models/PublicationStatus' + +const testRoleIds = [1, 2, 3, 4, 5, 6, 7, 8] +const testCollectionItemTypes = [CollectionItemType.DATASET, CollectionItemType.FILE] +const testPublishingStatuses = [ + PublicationStatus.Published, + PublicationStatus.Draft, + PublicationStatus.Unpublished +] + +describe('execute', () => { + beforeAll(() => { + ApiConfig.init( + TestConstants.TEST_API_URL, + DataverseApiAuthMechanism.API_KEY, + process.env.TEST_API_KEY + ) + }) + test('should return ? when repository returns empty item subset', async () => { + expect.assertions(2) + let readError: ReadError | undefined = undefined + try { + await getMyDataCollectionItems.execute( + testRoleIds, + testCollectionItemTypes, + testPublishingStatuses, + undefined, + undefined, + undefined + ) + throw new Error('Use case should throw an error') + } catch (error) { + readError = error as ReadError + } finally { + expect(readError).toBeInstanceOf(ReadError) + expect(readError?.message).toEqual( + 'There was an error when reading the resource. Reason was: Sorry, no results were found.' + ) + } + }), + describe('test with created collection items', () => { + const testCollectionAlias = 'collectionsRepositoryFunctionalTestCollection' + let testDatasetIds: CreatedDatasetIdentifiers + const testTextFile1Name = 'test-file-1.txt' + + beforeAll(async () => { + await createCollectionViaApi(testCollectionAlias) + try { + testDatasetIds = await createDataset.execute( + TestConstants.TEST_NEW_DATASET_DTO, + testCollectionAlias + ) + } catch (error) { + throw new Error('Tests beforeAll(): Error while creating test dataset') + } + await uploadFileViaApi(testDatasetIds.numericId, testTextFile1Name).catch(() => { + throw new Error(`Tests beforeAll(): Error while uploading file ${testTextFile1Name}`) + }) + }) + + afterAll(async () => { + try { + await deleteUnpublishedDatasetViaApi(testDatasetIds.numericId) + } catch (error) { + throw new Error('Tests afterAll(): Error while deleting test dataset') + } + try { + await deleteCollectionViaApi(testCollectionAlias) + } catch (error) { + throw new Error('Tests afterAll(): Error while deleting test collection') + } + }) + + test('should return items when valid roles,collection types, and publishingStatuses are provided', async () => { + // Give enough time to Solr for indexing + await new Promise((resolve) => setTimeout(resolve, 5000)) + + try { + const actual = await getMyDataCollectionItems.execute( + testRoleIds, + testCollectionItemTypes, + testPublishingStatuses + ) + + const actualFilePreview = actual.items[1] as FilePreview + const actualDatasetPreview = actual.items[0] as DatasetPreview + + expect(actualFilePreview.name).toBe('test-file-1.txt') + expect(actualDatasetPreview.title).toBe( + 'Dataset created using the createDataset use case' + ) + + expect(actual.totalItemCount).toBe(2) + } catch (error) { + throw new Error('Item subset should be retrieved') + } + }) + + test('should return an error message when no role is specified', async () => { + expect.assertions(2) + let readError: ReadError | undefined = undefined + try { + await getMyDataCollectionItems.execute([], [], [], undefined, undefined, undefined) + throw new Error('Use case should throw an error') + } catch (error) { + readError = error as ReadError + } finally { + expect(readError).toBeInstanceOf(ReadError) + expect(readError?.message).toEqual( + `There was an error when reading the resource. Reason was: No results. Please select at least one Role.` + ) + } + }) + test('should return an error message when no publication status is specified', async () => { + expect.assertions(2) + let readError: ReadError | undefined = undefined + try { + await getMyDataCollectionItems.execute( + testRoleIds, + testCollectionItemTypes, + [], + undefined, + undefined, + undefined + ) + throw new Error('Use case should throw an error') + } catch (error) { + readError = error as ReadError + } finally { + expect(readError).toBeInstanceOf(ReadError) + expect(readError?.message).toEqual( + `There was an error when reading the resource. Reason was: No user found for: "Published, Unpublished, Draft, In Review, Deaccessioned"` + ) + } + }) + test('should an error message when no collection type is specified', async () => { + expect.assertions(2) + let readError: ReadError | undefined = undefined + try { + await getMyDataCollectionItems.execute( + testRoleIds, + testCollectionItemTypes, + [], + undefined, + undefined, + undefined + ) + throw new Error('Use case should throw an error') + } catch (error) { + readError = error as ReadError + } finally { + expect(readError).toBeInstanceOf(ReadError) + expect(readError?.message).toEqual( + `There was an error when reading the resource. Reason was: No user found for: "Published, Unpublished, Draft, In Review, Deaccessioned"` + ) + } + }) + }) +}) diff --git a/test/unit/collections/GetCollectionItemsByUserRole.test.ts b/test/unit/collections/GetMyDataCollectionItems.test.ts similarity index 72% rename from test/unit/collections/GetCollectionItemsByUserRole.test.ts rename to test/unit/collections/GetMyDataCollectionItems.test.ts index f300d175..d6be2650 100644 --- a/test/unit/collections/GetCollectionItemsByUserRole.test.ts +++ b/test/unit/collections/GetMyDataCollectionItems.test.ts @@ -5,28 +5,51 @@ import { CollectionItemType } from '../../../dist' import { createDatasetPreviewModel } from '../../testHelpers/datasets/datasetPreviewHelper' import { createFilePreviewModel } from '../../testHelpers/files/filePreviewHelper' import { createCollectionPreviewModel } from '../../testHelpers/collections/collectionPreviewHelper' -import { MyDataCollectionItemSubset } from '../../../src/collections/domain/models/CollectionItemSubset' +import { + CollectionItemsFacetLabel, + MyDataCollectionItemSubset +} from '../../../src/collections/domain/models/CollectionItemSubset' +import { PublicationStatus } from '../../../dist/core/domain/models/PublicationStatus' -describe('GetCollectionItemsByUserRole', () => { +describe('GetMyDataCollectionItems', () => { let collectionRepositoryStub: ICollectionsRepository let testGetMyDataCollectionItems: GetMyDataCollectionItems const testRoleIds = [1, 2] const testCollectionItemTypes = [CollectionItemType.DATASET, CollectionItemType.FILE] + const testPublishingStatuses = [ + PublicationStatus.Published, + PublicationStatus.Draft, + PublicationStatus.Unpublished + ] const testLimit = 10 const testPage = 1 const testSearchText = 'test' + const testOtherUserName = 'testUser' const testItems = [ createDatasetPreviewModel(), createFilePreviewModel(), createCollectionPreviewModel() ] - + const testFacets = [ + { + name: 'Published', + count: 10 + }, + { + name: 'Draft', + count: 5 + }, + { + name: 'Unpublished', + count: 15 + } + ] as CollectionItemsFacetLabel[] const testItemSubset: MyDataCollectionItemSubset = { items: testItems, publishingFacet: testFacets, - totalItemCount: testTotalCount, - countPerObjectType: testCountPerObjectType + totalItemCount: 30, + countPerObjectType: { dataverses: 10, datasets: 15, files: 5 } } beforeEach(() => { collectionRepositoryStub = {} as ICollectionsRepository @@ -39,6 +62,7 @@ describe('GetCollectionItemsByUserRole', () => { const actual = await testGetMyDataCollectionItems.execute( testRoleIds, testCollectionItemTypes, + testPublishingStatuses, testLimit, testPage, testSearchText @@ -54,6 +78,7 @@ describe('GetCollectionItemsByUserRole', () => { testGetMyDataCollectionItems.execute( testRoleIds, testCollectionItemTypes, + testPublishingStatuses, testLimit, testPage, testSearchText @@ -61,29 +86,20 @@ describe('GetCollectionItemsByUserRole', () => { ).rejects.toThrow(ReadError) }) - test('should handle roleIds parameter', async () => { + test('should handle required parameters', async () => { collectionRepositoryStub.getMyDataCollectionItems = jest.fn().mockResolvedValue(testItemSubset) - const actual = await testGetMyDataCollectionItems.execute(testRoleIds, [], undefined) - - expect(collectionRepositoryStub.getMyDataCollectionItems).toHaveBeenCalledWith( + const actual = await testGetMyDataCollectionItems.execute( testRoleIds, - [], - undefined, - undefined, - undefined + testCollectionItemTypes, + testPublishingStatuses ) - expect(actual).toEqual(testItemSubset) - }) - - test('should handle collectionItemTypes parameter', async () => { - collectionRepositoryStub.getMyDataCollectionItems = jest.fn().mockResolvedValue(testItemSubset) - - const actual = await testGetMyDataCollectionItems.execute([], testCollectionItemTypes) expect(collectionRepositoryStub.getMyDataCollectionItems).toHaveBeenCalledWith( - [], + testRoleIds, testCollectionItemTypes, + testPublishingStatuses, + undefined, undefined, undefined, undefined @@ -97,9 +113,11 @@ describe('GetCollectionItemsByUserRole', () => { const actual = await testGetMyDataCollectionItems.execute( testRoleIds, testCollectionItemTypes, + testPublishingStatuses, testLimit, testPage, - testSearchText + testSearchText, + testOtherUserName ) expect(collectionRepositoryStub.getMyDataCollectionItems).toHaveBeenCalledWith( @@ -107,7 +125,8 @@ describe('GetCollectionItemsByUserRole', () => { testCollectionItemTypes, testLimit, testPage, - testSearchText + testSearchText, + testOtherUserName ) expect(actual).toEqual(testItemSubset) }) From 722df10fe41bed7315be9614d1077062e48c8160 Mon Sep 17 00:00:00 2001 From: Ellen Kraffmiller Date: Thu, 24 Apr 2025 17:21:15 -0400 Subject: [PATCH 03/28] fix: imports --- docs/useCases.md | 58 +++++++++++++++++++ .../repositories/ICollectionsRepository.ts | 4 +- .../useCases/GetMyDataCollectionItems.ts | 4 +- .../repositories/CollectionsRepository.ts | 2 +- .../transformers/MyDataFilePreviewPayload.ts | 2 +- .../GetMyDataCollectionItems.test.ts | 4 +- .../GetMyDataCollectionItems.test.ts | 4 +- 7 files changed, 68 insertions(+), 10 deletions(-) diff --git a/docs/useCases.md b/docs/useCases.md index e8d7be92..afe78846 100644 --- a/docs/useCases.md +++ b/docs/useCases.md @@ -215,6 +215,64 @@ 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 [MyDataCollectionItemSubset](../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 offset = 20 +const searchText = 'search text' +const otherUserName = 'otherUserName' + +getCollectionItems + .execute( + roleIds, + collectionItemTypes, + publishingStatuses, + limit, + offset, + searchText, + otherUserName + ) + .then((subset: MyDataCollectionItemSubset) => { + /* ... */ + }) + +/* ... */ +``` + +_See [use case](../src/collections/domain/useCases/GetMyDataCollectionItems.ts) implementation_. + +The `roleIds` parameter is an array of role identifiers that the user has in the collection items. At least one roleId must be specified. +The `collectionItemTypes` parameter is an array of collection item types to filter the results. At least one collectionItemType must be specified. +The `publishingStatuses` parameter 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: + +The `searchText` parameter is an optional string to filter the results by. +The `otherUserName` parameter 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 `MyDataCollectionItemSubset`returned instance contains a property called `totalItemCount` which is necessary for pagination. + +This use case supports the following optional parameters depending on the search goals: + +- **limit**: (number) Limit for pagination. +- **offset**: (number) Offset for pagination. +- **collectionSearchCriteria**: ([CollectionSearchCriteria](../src/collections/domain/models/CollectionSearchCriteria.ts)) Supports filtering the collection items by different properties. + #### 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. diff --git a/src/collections/domain/repositories/ICollectionsRepository.ts b/src/collections/domain/repositories/ICollectionsRepository.ts index 32de5c36..4b083611 100644 --- a/src/collections/domain/repositories/ICollectionsRepository.ts +++ b/src/collections/domain/repositories/ICollectionsRepository.ts @@ -6,8 +6,8 @@ import { CollectionFeaturedItem } from '../models/CollectionFeaturedItem' import { CollectionItemSubset, MyDataCollectionItemSubset } from '../models/CollectionItemSubset' import { CollectionSearchCriteria } from '../models/CollectionSearchCriteria' import { CollectionUserPermissions } from '../models/CollectionUserPermissions' -import { CollectionItemType } from '../../../../dist' -import { PublicationStatus } from '../../../../dist/core/domain/models/PublicationStatus' +import { PublicationStatus } from '../../../../src/core/domain/models/PublicationStatus' +import { CollectionItemType } from '../../../../src/collections/domain/models/CollectionItemType' export interface ICollectionsRepository { getCollection(collectionIdOrAlias: number | string): Promise diff --git a/src/collections/domain/useCases/GetMyDataCollectionItems.ts b/src/collections/domain/useCases/GetMyDataCollectionItems.ts index 01bcd773..204a2a5e 100644 --- a/src/collections/domain/useCases/GetMyDataCollectionItems.ts +++ b/src/collections/domain/useCases/GetMyDataCollectionItems.ts @@ -1,8 +1,8 @@ import { UseCase } from '../../../core/domain/useCases/UseCase' import { MyDataCollectionItemSubset } from '../models/CollectionItemSubset' import { ICollectionsRepository } from '../repositories/ICollectionsRepository' -import { CollectionItemType } from '../../../../dist' -import { PublicationStatus } from '../../../../dist/core/domain/models/PublicationStatus' +import { CollectionItemType } from '../../../../src/collections/domain/models/CollectionItemType' +import { PublicationStatus } from '../../../../src/core/domain/models/PublicationStatus' export class GetMyDataCollectionItems implements UseCase { private collectionsRepository: ICollectionsRepository diff --git a/src/collections/infra/repositories/CollectionsRepository.ts b/src/collections/infra/repositories/CollectionsRepository.ts index 2df25090..572eb335 100644 --- a/src/collections/infra/repositories/CollectionsRepository.ts +++ b/src/collections/infra/repositories/CollectionsRepository.ts @@ -25,7 +25,7 @@ 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 '../../../../dist/core/domain/models/PublicationStatus' +import { PublicationStatus } from '../../../../src/core/domain/models/PublicationStatus' import { ReadError } from '../../../core/domain/repositories/ReadError' export interface NewCollectionRequestPayload { diff --git a/src/files/infra/repositories/transformers/MyDataFilePreviewPayload.ts b/src/files/infra/repositories/transformers/MyDataFilePreviewPayload.ts index a8a28fa4..a05b19e4 100644 --- a/src/files/infra/repositories/transformers/MyDataFilePreviewPayload.ts +++ b/src/files/infra/repositories/transformers/MyDataFilePreviewPayload.ts @@ -1,4 +1,4 @@ -import { FilePreviewChecksumPayload } from '../../../../../dist/files/infra/repositories/transformers/FilePreviewPayload' +import { FilePreviewChecksumPayload } from '../../../../../src/files/infra/repositories/transformers/FilePreviewPayload' export interface MyDataFilePreviewPayload { name: string diff --git a/test/functional/collections/GetMyDataCollectionItems.test.ts b/test/functional/collections/GetMyDataCollectionItems.test.ts index ff948992..06344d03 100644 --- a/test/functional/collections/GetMyDataCollectionItems.test.ts +++ b/test/functional/collections/GetMyDataCollectionItems.test.ts @@ -15,8 +15,8 @@ import { } from '../../testHelpers/collections/collectionHelper' import { uploadFileViaApi } from '../../testHelpers/files/filesHelper' import { deleteUnpublishedDatasetViaApi } from '../../testHelpers/datasets/datasetHelper' -import { CollectionItemType } from '../../../dist' -import { PublicationStatus } from '../../../dist/core/domain/models/PublicationStatus' +import { CollectionItemType } from '../../../src/collections/domain/models/CollectionItemType' +import { PublicationStatus } from '../../../src/core/domain/models/PublicationStatus' const testRoleIds = [1, 2, 3, 4, 5, 6, 7, 8] const testCollectionItemTypes = [CollectionItemType.DATASET, CollectionItemType.FILE] diff --git a/test/unit/collections/GetMyDataCollectionItems.test.ts b/test/unit/collections/GetMyDataCollectionItems.test.ts index d6be2650..67ba1f37 100644 --- a/test/unit/collections/GetMyDataCollectionItems.test.ts +++ b/test/unit/collections/GetMyDataCollectionItems.test.ts @@ -1,7 +1,7 @@ import { ICollectionsRepository } from '../../../src/collections/domain/repositories/ICollectionsRepository' import { GetMyDataCollectionItems } from '../../../src/collections/domain/useCases/GetMyDataCollectionItems' import { ReadError } from '../../../src' -import { CollectionItemType } from '../../../dist' +import { CollectionItemType } from '../../../src/collections/domain/models/CollectionItemType' import { createDatasetPreviewModel } from '../../testHelpers/datasets/datasetPreviewHelper' import { createFilePreviewModel } from '../../testHelpers/files/filePreviewHelper' import { createCollectionPreviewModel } from '../../testHelpers/collections/collectionPreviewHelper' @@ -9,7 +9,7 @@ import { CollectionItemsFacetLabel, MyDataCollectionItemSubset } from '../../../src/collections/domain/models/CollectionItemSubset' -import { PublicationStatus } from '../../../dist/core/domain/models/PublicationStatus' +import { PublicationStatus } from '../../../src/core/domain/models/PublicationStatus' describe('GetMyDataCollectionItems', () => { let collectionRepositoryStub: ICollectionsRepository From 84259da7ef14b830f3e3e7c52ac5e326998bee28 Mon Sep 17 00:00:00 2001 From: Ellen Kraffmiller Date: Thu, 24 Apr 2025 22:08:03 -0400 Subject: [PATCH 04/28] fix: unit test, remove logs --- jest.config.functional.ts | 5 +++-- src/collections/infra/repositories/CollectionsRepository.ts | 2 -- .../repositories/transformers/collectionTransformers.ts | 3 +-- test/unit/collections/GetMyDataCollectionItems.test.ts | 1 + 4 files changed, 5 insertions(+), 6 deletions(-) diff --git a/jest.config.functional.ts b/jest.config.functional.ts index 01a71ab8..03cc29c3 100644 --- a/jest.config.functional.ts +++ b/jest.config.functional.ts @@ -1,6 +1,7 @@ import config from './jest.config' -config.modulePathIgnorePatterns = ['/test/unit', '/test/integration'] -console.log('RUNNING FUNCTIONAL TESTS') +config.modulePathIgnorePatterns = ['/test/unit', '/test/integration']( + 'RUNNING FUNCTIONAL TESTS' +) export default config diff --git a/src/collections/infra/repositories/CollectionsRepository.ts b/src/collections/infra/repositories/CollectionsRepository.ts index 572eb335..8b03660d 100644 --- a/src/collections/infra/repositories/CollectionsRepository.ts +++ b/src/collections/infra/repositories/CollectionsRepository.ts @@ -267,8 +267,6 @@ export class CollectionsRepository extends ApiRepository implements ICollections return transformMyDataResponseToCollectionItemSubset(response) }) .catch((error) => { - console.error('Error in getMyDataCollectionItems:', error) - console.log('Error response:', error.response) throw error }) } diff --git a/src/collections/infra/repositories/transformers/collectionTransformers.ts b/src/collections/infra/repositories/transformers/collectionTransformers.ts index cbc920fe..c3523cea 100644 --- a/src/collections/infra/repositories/transformers/collectionTransformers.ts +++ b/src/collections/infra/repositories/transformers/collectionTransformers.ts @@ -151,9 +151,8 @@ export const transformCollectionItemsResponseToCollectionItemSubset = ( export const transformMyDataResponseToCollectionItemSubset = ( response: AxiosResponse ): MyDataCollectionItemSubset => { - console.log('response', JSON.stringify(response.data)) const responseDataPayload = response.data.data - const itemsPayload = responseDataPayload.items ?? [] + const itemsPayload = responseDataPayload.items const countPerObjectTypePayload = responseDataPayload[ 'dvobject_counts' ] as MyDataCountPerObjectTypePayload diff --git a/test/unit/collections/GetMyDataCollectionItems.test.ts b/test/unit/collections/GetMyDataCollectionItems.test.ts index 67ba1f37..f054e013 100644 --- a/test/unit/collections/GetMyDataCollectionItems.test.ts +++ b/test/unit/collections/GetMyDataCollectionItems.test.ts @@ -123,6 +123,7 @@ describe('GetMyDataCollectionItems', () => { expect(collectionRepositoryStub.getMyDataCollectionItems).toHaveBeenCalledWith( testRoleIds, testCollectionItemTypes, + testPublishingStatuses, testLimit, testPage, testSearchText, From 4639b8639492122c18bc2808fb2fd2e047d59b6e Mon Sep 17 00:00:00 2001 From: Ellen Kraffmiller Date: Thu, 24 Apr 2025 22:39:59 -0400 Subject: [PATCH 05/28] fix: functional test --- jest.config.functional.ts | 6 +- .../GetMyDataCollectionItems.test.ts | 231 +++++++++--------- 2 files changed, 114 insertions(+), 123 deletions(-) diff --git a/jest.config.functional.ts b/jest.config.functional.ts index 03cc29c3..db0c2c93 100644 --- a/jest.config.functional.ts +++ b/jest.config.functional.ts @@ -1,7 +1,5 @@ import config from './jest.config' -config.modulePathIgnorePatterns = ['/test/unit', '/test/integration']( - 'RUNNING FUNCTIONAL TESTS' -) - +config.modulePathIgnorePatterns = ['/test/unit', '/test/integration'] +console.log('RUNNING FUNCTIONAL TESTS') export default config diff --git a/test/functional/collections/GetMyDataCollectionItems.test.ts b/test/functional/collections/GetMyDataCollectionItems.test.ts index 06344d03..95827e6a 100644 --- a/test/functional/collections/GetMyDataCollectionItems.test.ts +++ b/test/functional/collections/GetMyDataCollectionItems.test.ts @@ -1,11 +1,11 @@ import { ApiConfig, + createDataset, CreatedDatasetIdentifiers, DatasetPreview, FilePreview, - ReadError, - createDataset, - getMyDataCollectionItems + getMyDataCollectionItems, + ReadError } from '../../../src' import { TestConstants } from '../../testHelpers/TestConstants' import { DataverseApiAuthMechanism } from '../../../src/core/infra/repositories/ApiConfig' @@ -27,21 +27,50 @@ const testPublishingStatuses = [ ] describe('execute', () => { - beforeAll(() => { + const testCollectionAlias = 'collectionsRepositoryFunctionalTestCollection' + let testDatasetIds: CreatedDatasetIdentifiers + const testTextFile1Name = 'test-file-1.txt' + + beforeAll(async () => { ApiConfig.init( TestConstants.TEST_API_URL, DataverseApiAuthMechanism.API_KEY, process.env.TEST_API_KEY ) + await createCollectionViaApi(testCollectionAlias) + try { + testDatasetIds = await createDataset.execute( + TestConstants.TEST_NEW_DATASET_DTO, + testCollectionAlias + ) + } catch (error) { + throw new Error('Tests beforeAll(): Error while creating test dataset') + } + await uploadFileViaApi(testDatasetIds.numericId, testTextFile1Name).catch(() => { + throw new Error(`Tests beforeAll(): Error while uploading file ${testTextFile1Name}`) + }) + }) + + afterAll(async () => { + try { + await deleteUnpublishedDatasetViaApi(testDatasetIds.numericId) + } catch (error) { + throw new Error('Tests afterAll(): Error while deleting test dataset') + } + try { + await deleteCollectionViaApi(testCollectionAlias) + } catch (error) { + throw new Error('Tests afterAll(): Error while deleting test collection') + } }) - test('should return ? when repository returns empty item subset', async () => { + test('should return error message when repository returns empty item subset', async () => { expect.assertions(2) let readError: ReadError | undefined = undefined try { await getMyDataCollectionItems.execute( testRoleIds, testCollectionItemTypes, - testPublishingStatuses, + [PublicationStatus.Deaccessioned], undefined, undefined, undefined @@ -56,122 +85,86 @@ describe('execute', () => { ) } }), - describe('test with created collection items', () => { - const testCollectionAlias = 'collectionsRepositoryFunctionalTestCollection' - let testDatasetIds: CreatedDatasetIdentifiers - const testTextFile1Name = 'test-file-1.txt' - - beforeAll(async () => { - await createCollectionViaApi(testCollectionAlias) - try { - testDatasetIds = await createDataset.execute( - TestConstants.TEST_NEW_DATASET_DTO, - testCollectionAlias - ) - } catch (error) { - throw new Error('Tests beforeAll(): Error while creating test dataset') - } - await uploadFileViaApi(testDatasetIds.numericId, testTextFile1Name).catch(() => { - throw new Error(`Tests beforeAll(): Error while uploading file ${testTextFile1Name}`) - }) - }) - - afterAll(async () => { - try { - await deleteUnpublishedDatasetViaApi(testDatasetIds.numericId) - } catch (error) { - throw new Error('Tests afterAll(): Error while deleting test dataset') - } - try { - await deleteCollectionViaApi(testCollectionAlias) - } catch (error) { - throw new Error('Tests afterAll(): Error while deleting test collection') - } - }) + test('should return items when valid roles,collection types, and publishingStatuses are provided', async () => { + // Give enough time to Solr for indexing + await new Promise((resolve) => setTimeout(resolve, 5000)) - test('should return items when valid roles,collection types, and publishingStatuses are provided', async () => { - // Give enough time to Solr for indexing - await new Promise((resolve) => setTimeout(resolve, 5000)) + try { + const actual = await getMyDataCollectionItems.execute( + testRoleIds, + testCollectionItemTypes, + testPublishingStatuses + ) - try { - const actual = await getMyDataCollectionItems.execute( - testRoleIds, - testCollectionItemTypes, - testPublishingStatuses - ) + const actualFilePreview = actual.items[1] as FilePreview + const actualDatasetPreview = actual.items[0] as DatasetPreview - const actualFilePreview = actual.items[1] as FilePreview - const actualDatasetPreview = actual.items[0] as DatasetPreview + expect(actualFilePreview.name).toBe('test-file-1.txt') + expect(actualDatasetPreview.title).toBe('Dataset created using the createDataset use case') - expect(actualFilePreview.name).toBe('test-file-1.txt') - expect(actualDatasetPreview.title).toBe( - 'Dataset created using the createDataset use case' - ) - - expect(actual.totalItemCount).toBe(2) - } catch (error) { - throw new Error('Item subset should be retrieved') - } - }) - - test('should return an error message when no role is specified', async () => { - expect.assertions(2) - let readError: ReadError | undefined = undefined - try { - await getMyDataCollectionItems.execute([], [], [], undefined, undefined, undefined) - throw new Error('Use case should throw an error') - } catch (error) { - readError = error as ReadError - } finally { - expect(readError).toBeInstanceOf(ReadError) - expect(readError?.message).toEqual( - `There was an error when reading the resource. Reason was: No results. Please select at least one Role.` - ) - } - }) - test('should return an error message when no publication status is specified', async () => { - expect.assertions(2) - let readError: ReadError | undefined = undefined - try { - await getMyDataCollectionItems.execute( - testRoleIds, - testCollectionItemTypes, - [], - undefined, - undefined, - undefined - ) - throw new Error('Use case should throw an error') - } catch (error) { - readError = error as ReadError - } finally { - expect(readError).toBeInstanceOf(ReadError) - expect(readError?.message).toEqual( - `There was an error when reading the resource. Reason was: No user found for: "Published, Unpublished, Draft, In Review, Deaccessioned"` - ) - } - }) - test('should an error message when no collection type is specified', async () => { - expect.assertions(2) - let readError: ReadError | undefined = undefined - try { - await getMyDataCollectionItems.execute( - testRoleIds, - testCollectionItemTypes, - [], - undefined, - undefined, - undefined - ) - throw new Error('Use case should throw an error') - } catch (error) { - readError = error as ReadError - } finally { - expect(readError).toBeInstanceOf(ReadError) - expect(readError?.message).toEqual( - `There was an error when reading the resource. Reason was: No user found for: "Published, Unpublished, Draft, In Review, Deaccessioned"` - ) - } - }) + expect(actual.totalItemCount).toBe(2) + } catch (error) { + throw new Error('Item subset should be retrieved') + } }) + + test('should return an error message when no role is specified', async () => { + expect.assertions(2) + let readError: ReadError | undefined = undefined + try { + await getMyDataCollectionItems.execute([], [], [], undefined, undefined, undefined) + throw new Error('Use case should throw an error') + } catch (error) { + readError = error as ReadError + } finally { + expect(readError).toBeInstanceOf(ReadError) + expect(readError?.message).toEqual( + `There was an error when reading the resource. Reason was: No results. Please select at least one Role.` + ) + } + }) + test('should return an error message when no publication status is specified', async () => { + expect.assertions(2) + let readError: ReadError | undefined = undefined + try { + await getMyDataCollectionItems.execute( + testRoleIds, + testCollectionItemTypes, + [], + undefined, + undefined, + undefined + ) + throw new Error('Use case should throw an error') + } catch (error) { + readError = error as ReadError + } finally { + expect(readError).toBeInstanceOf(ReadError) + expect(readError?.message).toEqual( + `There was an error when reading the resource. Reason was: No user found for: "Published, Unpublished, Draft, In Review, Deaccessioned"` + ) + } + }) + test('should an error message when no collection type is specified', async () => { + expect.assertions(2) + let readError: ReadError | undefined = undefined + try { + await getMyDataCollectionItems.execute( + testRoleIds, + testCollectionItemTypes, + [], + undefined, + undefined, + undefined + ) + throw new Error('Use case should throw an error') + } catch (error) { + readError = error as ReadError + } finally { + expect(readError).toBeInstanceOf(ReadError) + expect(readError?.message).toEqual( + `There was an error when reading the resource. Reason was: No user found for: "Published, Unpublished, Draft, In Review, Deaccessioned"` + ) + } + }) }) From 9e79bb49c4a276de6a455215a126bda5f45c04c6 Mon Sep 17 00:00:00 2001 From: Ellen Kraffmiller Date: Fri, 25 Apr 2025 08:39:25 -0400 Subject: [PATCH 06/28] change test data --- .../collections/GetMyDataCollectionItems.test.ts | 7 ++++--- test/functional/datasets/PublishDataset.test.ts | 2 +- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/test/functional/collections/GetMyDataCollectionItems.test.ts b/test/functional/collections/GetMyDataCollectionItems.test.ts index 95827e6a..13313d43 100644 --- a/test/functional/collections/GetMyDataCollectionItems.test.ts +++ b/test/functional/collections/GetMyDataCollectionItems.test.ts @@ -27,9 +27,9 @@ const testPublishingStatuses = [ ] describe('execute', () => { - const testCollectionAlias = 'collectionsRepositoryFunctionalTestCollection' + const testCollectionAlias = 'collectionsRepositoryGetMyDataCollection' let testDatasetIds: CreatedDatasetIdentifiers - const testTextFile1Name = 'test-file-1.txt' + const testTextFile1Name = 'test-file-2.txt' beforeAll(async () => { ApiConfig.init( @@ -99,11 +99,12 @@ describe('execute', () => { const actualFilePreview = actual.items[1] as FilePreview const actualDatasetPreview = actual.items[0] as DatasetPreview - expect(actualFilePreview.name).toBe('test-file-1.txt') + expect(actualFilePreview.name).toBe(testTextFile1Name) expect(actualDatasetPreview.title).toBe('Dataset created using the createDataset use case') expect(actual.totalItemCount).toBe(2) } catch (error) { + console.log(error) throw new Error('Item subset should be retrieved') } }) diff --git a/test/functional/datasets/PublishDataset.test.ts b/test/functional/datasets/PublishDataset.test.ts index 9bdbfdb6..f4b22be0 100644 --- a/test/functional/datasets/PublishDataset.test.ts +++ b/test/functional/datasets/PublishDataset.test.ts @@ -67,7 +67,7 @@ describe('execute', () => { createdDatasetIdentifiers.persistentId, VersionUpdateType.MAJOR ) - await waitForNoLocks(createdDatasetIdentifiers.numericId, 10) + await waitForNoLocks(createdDatasetIdentifiers.numericId, 30) expect(response).toBeUndefined() await deletePublishedDatasetViaApi(createdDatasetIdentifiers.persistentId) From c57cc01e8b15949234ffc20561811869299b5bc8 Mon Sep 17 00:00:00 2001 From: Ellen Kraffmiller Date: Fri, 25 Apr 2025 11:03:40 -0400 Subject: [PATCH 07/28] fix search query param --- .../infra/repositories/CollectionsRepository.ts | 10 ++++++---- .../collections/GetMyDataCollectionItems.test.ts | 13 ++++++------- 2 files changed, 12 insertions(+), 11 deletions(-) diff --git a/src/collections/infra/repositories/CollectionsRepository.ts b/src/collections/infra/repositories/CollectionsRepository.ts index 8b03660d..e96db662 100644 --- a/src/collections/infra/repositories/CollectionsRepository.ts +++ b/src/collections/infra/repositories/CollectionsRepository.ts @@ -70,14 +70,13 @@ export enum GetCollectionItemsQueryParams { } export enum GetMyDataCollectionItemsQueryParams { - QUERY = 'q', + 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', - SHOW_TYPE_COUNTS = 'show_type_counts' + USER_IDENTIFIER = 'userIdentifier' } export class CollectionsRepository extends ApiRepository implements ICollectionsRepository { @@ -225,7 +224,10 @@ export class CollectionsRepository extends ApiRepository implements ICollections } if (searchText) { - queryParams.set(GetMyDataCollectionItemsQueryParams.QUERY, encodeURIComponent(searchText)) + queryParams.set( + GetMyDataCollectionItemsQueryParams.SEARCH_TEXT, + encodeURIComponent(searchText) + ) } roleIds.forEach((roleId) => { diff --git a/test/functional/collections/GetMyDataCollectionItems.test.ts b/test/functional/collections/GetMyDataCollectionItems.test.ts index 13313d43..7e0e830f 100644 --- a/test/functional/collections/GetMyDataCollectionItems.test.ts +++ b/test/functional/collections/GetMyDataCollectionItems.test.ts @@ -2,7 +2,6 @@ import { ApiConfig, createDataset, CreatedDatasetIdentifiers, - DatasetPreview, FilePreview, getMyDataCollectionItems, ReadError @@ -93,16 +92,16 @@ describe('execute', () => { const actual = await getMyDataCollectionItems.execute( testRoleIds, testCollectionItemTypes, - testPublishingStatuses + testPublishingStatuses, + undefined, + undefined, + testTextFile1Name ) - const actualFilePreview = actual.items[1] as FilePreview - const actualDatasetPreview = actual.items[0] as DatasetPreview - + const actualFilePreview = actual.items[0] as FilePreview expect(actualFilePreview.name).toBe(testTextFile1Name) - expect(actualDatasetPreview.title).toBe('Dataset created using the createDataset use case') - expect(actual.totalItemCount).toBe(2) + expect(actual.totalItemCount).toBe(1) } catch (error) { console.log(error) throw new Error('Item subset should be retrieved') From edf04091dbd1d465b1c7a907caeee51a948ab5b0 Mon Sep 17 00:00:00 2001 From: Ellen Kraffmiller Date: Fri, 25 Apr 2025 11:37:17 -0400 Subject: [PATCH 08/28] search for collection alias --- .../collections/GetMyDataCollectionItems.test.ts | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/test/functional/collections/GetMyDataCollectionItems.test.ts b/test/functional/collections/GetMyDataCollectionItems.test.ts index 7e0e830f..5e8a6df4 100644 --- a/test/functional/collections/GetMyDataCollectionItems.test.ts +++ b/test/functional/collections/GetMyDataCollectionItems.test.ts @@ -2,7 +2,7 @@ import { ApiConfig, createDataset, CreatedDatasetIdentifiers, - FilePreview, + CollectionPreview, getMyDataCollectionItems, ReadError } from '../../../src' @@ -18,7 +18,11 @@ import { CollectionItemType } from '../../../src/collections/domain/models/Colle import { PublicationStatus } from '../../../src/core/domain/models/PublicationStatus' const testRoleIds = [1, 2, 3, 4, 5, 6, 7, 8] -const testCollectionItemTypes = [CollectionItemType.DATASET, CollectionItemType.FILE] +const testCollectionItemTypes = [ + CollectionItemType.COLLECTION, + CollectionItemType.DATASET, + CollectionItemType.FILE +] const testPublishingStatuses = [ PublicationStatus.Published, PublicationStatus.Draft, @@ -95,11 +99,11 @@ describe('execute', () => { testPublishingStatuses, undefined, undefined, - testTextFile1Name + testCollectionAlias ) - const actualFilePreview = actual.items[0] as FilePreview - expect(actualFilePreview.name).toBe(testTextFile1Name) + const actualCollectionPreview = actual.items[0] as CollectionPreview + expect(actualCollectionPreview.alias).toBe(testCollectionAlias) expect(actual.totalItemCount).toBe(1) } catch (error) { From 24ae72af907bd8693bc0e643e2a7e2e971efca56 Mon Sep 17 00:00:00 2001 From: Ellen Kraffmiller Date: Fri, 25 Apr 2025 14:26:24 -0400 Subject: [PATCH 09/28] add more tests of response data --- .../transformers/collectionTransformers.ts | 2 +- .../GetMyDataCollectionItems.test.ts | 27 +++++++++++++++++++ 2 files changed, 28 insertions(+), 1 deletion(-) diff --git a/src/collections/infra/repositories/transformers/collectionTransformers.ts b/src/collections/infra/repositories/transformers/collectionTransformers.ts index c3523cea..81a0cfac 100644 --- a/src/collections/infra/repositories/transformers/collectionTransformers.ts +++ b/src/collections/infra/repositories/transformers/collectionTransformers.ts @@ -216,7 +216,7 @@ const transformPublicationStatusResponseToLabels = ( const labels: CollectionItemsFacetLabel[] = [] labels.push({ name: 'Published', count: publicationStatusCounts.published_count }) labels.push({ name: 'Unpublished', count: publicationStatusCounts.unpublished_count }) - labels.push({ name: 'Draft ', count: publicationStatusCounts.draft_count }) + labels.push({ name: 'Draft', count: publicationStatusCounts.draft_count }) labels.push({ name: 'In Review', count: publicationStatusCounts.in_review_count }) labels.push({ name: 'Deaccessioned', count: publicationStatusCounts.deaccessioned_count }) return labels diff --git a/test/functional/collections/GetMyDataCollectionItems.test.ts b/test/functional/collections/GetMyDataCollectionItems.test.ts index 5e8a6df4..c4cc7f90 100644 --- a/test/functional/collections/GetMyDataCollectionItems.test.ts +++ b/test/functional/collections/GetMyDataCollectionItems.test.ts @@ -106,6 +106,33 @@ describe('execute', () => { expect(actualCollectionPreview.alias).toBe(testCollectionAlias) expect(actual.totalItemCount).toBe(1) + expect(actual.publishingFacet).toEqual([ + { + name: 'Published', + count: 0 + }, + { + name: 'Unpublished', + count: 1 + }, + { + name: 'Draft', + count: 0 + }, + { + name: 'In Review', + count: 0 + }, + { + name: 'Deaccessioned', + count: 0 + } + ]) + expect(actual.countPerObjectType).toEqual({ + dataverses: 1, + datasets: 0, + files: 0 + }) } catch (error) { console.log(error) throw new Error('Item subset should be retrieved') From 02c8f6bea949671e74ace3f34e592754f10c0067 Mon Sep 17 00:00:00 2001 From: Ellen Kraffmiller Date: Fri, 25 Apr 2025 15:05:22 -0400 Subject: [PATCH 10/28] update documentation --- docs/useCases.md | 13 +++++++------ .../domain/useCases/GetMyDataCollectionItems.ts | 3 ++- 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/docs/useCases.md b/docs/useCases.md index afe78846..53a14b37 100644 --- a/docs/useCases.md +++ b/docs/useCases.md @@ -256,22 +256,23 @@ getCollectionItems ``` _See [use case](../src/collections/domain/useCases/GetMyDataCollectionItems.ts) implementation_. +This use case requires the following parameters: -The `roleIds` parameter is an array of role identifiers that the user has in the collection items. At least one roleId must be specified. -The `collectionItemTypes` parameter is an array of collection item types to filter the results. At least one collectionItemType must be specified. -The `publishingStatuses` parameter is an array of publishing statuses to filter the results. At least one publishingStatus must be specified. +- **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: -The `searchText` parameter is an optional string to filter the results by. -The `otherUserName` parameter 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. +- **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 `MyDataCollectionItemSubset`returned instance contains a property called `totalItemCount` which is necessary for pagination. This use case supports the following optional parameters depending on the search goals: - **limit**: (number) Limit for pagination. - **offset**: (number) Offset for pagination. -- **collectionSearchCriteria**: ([CollectionSearchCriteria](../src/collections/domain/models/CollectionSearchCriteria.ts)) Supports filtering the collection items by different properties. #### Get Collection Featured Items diff --git a/src/collections/domain/useCases/GetMyDataCollectionItems.ts b/src/collections/domain/useCases/GetMyDataCollectionItems.ts index 204a2a5e..9ba834f4 100644 --- a/src/collections/domain/useCases/GetMyDataCollectionItems.ts +++ b/src/collections/domain/useCases/GetMyDataCollectionItems.ts @@ -16,9 +16,10 @@ export class GetMyDataCollectionItems implements UseCase} */ async execute( From 4a1f13e498805eb7ca4a58540df7b74a65beb302 Mon Sep 17 00:00:00 2001 From: Ellen Kraffmiller Date: Fri, 25 Apr 2025 15:18:26 -0400 Subject: [PATCH 11/28] fix documentation return value --- src/collections/domain/useCases/GetMyDataCollectionItems.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/collections/domain/useCases/GetMyDataCollectionItems.ts b/src/collections/domain/useCases/GetMyDataCollectionItems.ts index 9ba834f4..ad9368f6 100644 --- a/src/collections/domain/useCases/GetMyDataCollectionItems.ts +++ b/src/collections/domain/useCases/GetMyDataCollectionItems.ts @@ -20,7 +20,7 @@ export class GetMyDataCollectionItems implements UseCase} + * * @returns {Promise} */ async execute( roleIds: number[], From 89dc408df07979831183f575339eedfd27e9d416 Mon Sep 17 00:00:00 2001 From: Ellen Kraffmiller Date: Mon, 28 Apr 2025 14:39:23 -0400 Subject: [PATCH 12/28] fix parameter name to match API endpoint, --- docs/useCases.md | 6 +-- .../repositories/ICollectionsRepository.ts | 2 +- .../useCases/GetMyDataCollectionItems.ts | 4 +- .../repositories/CollectionsRepository.ts | 6 +-- .../transformers/MyDataFilePreviewPayload.ts | 50 ------------------- 5 files changed, 9 insertions(+), 59 deletions(-) diff --git a/docs/useCases.md b/docs/useCases.md index 53a14b37..4b7efff7 100644 --- a/docs/useCases.md +++ b/docs/useCases.md @@ -234,17 +234,17 @@ const publishingStatuses = [ PublicationStatus.Unpublished ] const limit = 10 -const offset = 20 +const selectedPage = 1 const searchText = 'search text' const otherUserName = 'otherUserName' -getCollectionItems +getMyDataCollectionItems .execute( roleIds, collectionItemTypes, publishingStatuses, limit, - offset, + selectedPage, searchText, otherUserName ) diff --git a/src/collections/domain/repositories/ICollectionsRepository.ts b/src/collections/domain/repositories/ICollectionsRepository.ts index 4b083611..a2cbc045 100644 --- a/src/collections/domain/repositories/ICollectionsRepository.ts +++ b/src/collections/domain/repositories/ICollectionsRepository.ts @@ -32,7 +32,7 @@ export interface ICollectionsRepository { collectionItemTypes: CollectionItemType[], publicationStatuses: PublicationStatus[], limit?: number, - page?: number, + selectedPage?: number, searchText?: string, otherUserName?: string ): Promise diff --git a/src/collections/domain/useCases/GetMyDataCollectionItems.ts b/src/collections/domain/useCases/GetMyDataCollectionItems.ts index ad9368f6..76623f52 100644 --- a/src/collections/domain/useCases/GetMyDataCollectionItems.ts +++ b/src/collections/domain/useCases/GetMyDataCollectionItems.ts @@ -17,8 +17,8 @@ export class GetMyDataCollectionItems implements UseCase} */ diff --git a/src/collections/infra/repositories/CollectionsRepository.ts b/src/collections/infra/repositories/CollectionsRepository.ts index e96db662..e6220347 100644 --- a/src/collections/infra/repositories/CollectionsRepository.ts +++ b/src/collections/infra/repositories/CollectionsRepository.ts @@ -209,7 +209,7 @@ export class CollectionsRepository extends ApiRepository implements ICollections collectionItemTypes: CollectionItemType[], publicationStatuses: PublicationStatus[], limit?: number, - page?: number, + selectedPage?: number, searchText?: string, userIdentifier?: string ): Promise { @@ -219,8 +219,8 @@ export class CollectionsRepository extends ApiRepository implements ICollections queryParams.set(GetMyDataCollectionItemsQueryParams.PER_PAGE, limit.toString()) } - if (page) { - queryParams.set(GetMyDataCollectionItemsQueryParams.SELECTED_PAGE, page.toString()) + if (selectedPage) { + queryParams.set(GetMyDataCollectionItemsQueryParams.SELECTED_PAGE, selectedPage.toString()) } if (searchText) { diff --git a/src/files/infra/repositories/transformers/MyDataFilePreviewPayload.ts b/src/files/infra/repositories/transformers/MyDataFilePreviewPayload.ts index a05b19e4..9aced9e4 100644 --- a/src/files/infra/repositories/transformers/MyDataFilePreviewPayload.ts +++ b/src/files/infra/repositories/transformers/MyDataFilePreviewPayload.ts @@ -38,53 +38,3 @@ export interface MyDataFilePreviewPayload { file_persistent_id?: string description?: string } -/* -{ - "name": "teacher_survey.tab", - "type": "file", - "url": "http://localhost:8080/api/access/datafile/15", - "file_id": "15", - "file_type": "Comma Separated Values", - "file_content_type": "text/comma-separated-values", - "size_in_bytes": 174644, - "md5": "1db96dd4229c5bf5e6d8b1e6a301dca0", - "checksum": { - "type": "MD5", - "value": "1db96dd4229c5bf5e6d8b1e6a301dca0" - }, - "unf": "UNF:6:2Q0xnmkmgp6vi/lAa+bhRQ==", - "dataset_name": "Never Published", - "dataset_id": "14", - "dataset_persistent_id": "doi:10.5072/FK2/IHYE1J", - "dataset_citation": "Admin, Dataverse, 2025, \"Never Published\", https://doi.org/10.5072/FK2/IHYE1J, Root, DRAFT VERSION, UNF:6:2Q0xnmkmgp6vi/lAa+bhRQ== [fileUNF]", - "restricted": false, - "variables": 10, - "observations": 7000, - "canDownloadFile": true, - "matches": [], - "score": 321.1235656738281, - "entity_id": 15, - "publicationStatuses": [ - "Unpublished", - "Draft" - ], - "releaseOrCreateDate": "2025-04-23T13:48:15Z", - "publication_statuses": [ - "Unpublished", - "Draft" - ], - "is_draft_state": true, - "is_in_review_state": false, - "is_unpublished_state": true, - "is_published": false, - "is_deaccesioned": false, - "is_valid": true, - "date_to_display_on_card": "Apr 23, 2025", - "parentIdentifier": "doi:10.5072/FK2/IHYE1J", - "parentName": "Never Published", - "user_roles": [ - "Admin", - "Contributor" - ] -} -*/ From 34b2588fde6d68c67cde31a6e48dfb7bbcd536c6 Mon Sep 17 00:00:00 2001 From: Ellen Kraffmiller Date: Mon, 28 Apr 2025 16:49:33 -0400 Subject: [PATCH 13/28] in countPerObjectType, change key to from 'dataverses' to 'collections' --- src/collections/domain/models/CollectionItemSubset.ts | 2 +- .../infra/repositories/transformers/collectionTransformers.ts | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/collections/domain/models/CollectionItemSubset.ts b/src/collections/domain/models/CollectionItemSubset.ts index 6775de4d..16589588 100644 --- a/src/collections/domain/models/CollectionItemSubset.ts +++ b/src/collections/domain/models/CollectionItemSubset.ts @@ -27,7 +27,7 @@ export interface CollectionItemsFacetLabel { } interface CountPerObjectType { - dataverses: number + collections: number datasets: number files: number } diff --git a/src/collections/infra/repositories/transformers/collectionTransformers.ts b/src/collections/infra/repositories/transformers/collectionTransformers.ts index 81a0cfac..185d2ac2 100644 --- a/src/collections/infra/repositories/transformers/collectionTransformers.ts +++ b/src/collections/infra/repositories/transformers/collectionTransformers.ts @@ -135,7 +135,7 @@ export const transformCollectionItemsResponseToCollectionItemSubset = ( ) const countPerObjectType = { - dataverses: countPerObjectTypePayload['Dataverses'], + collections: countPerObjectTypePayload['Dataverses'], datasets: countPerObjectTypePayload['Datasets'], files: countPerObjectTypePayload['Files'] } @@ -185,7 +185,7 @@ export const transformMyDataResponseToCollectionItemSubset = ( }) const countPerObjectType = { - dataverses: countPerObjectTypePayload['dataverses_count'], + collections: countPerObjectTypePayload['dataverses_count'], datasets: countPerObjectTypePayload['datasets_count'], files: countPerObjectTypePayload['files_count'] } From 4519702010e2b974b34da01e4717f49cb3107fcd Mon Sep 17 00:00:00 2001 From: Ellen Kraffmiller Date: Mon, 28 Apr 2025 21:22:36 -0400 Subject: [PATCH 14/28] update countPerObjectType in tests --- .../GetMyDataCollectionItems.test.ts | 2 +- .../collections/CollectionsRepository.test.ts | 32 +++++++++---------- .../collections/CollectionsRepository.test.ts | 2 +- 3 files changed, 18 insertions(+), 18 deletions(-) diff --git a/test/functional/collections/GetMyDataCollectionItems.test.ts b/test/functional/collections/GetMyDataCollectionItems.test.ts index c4cc7f90..a7e0e69b 100644 --- a/test/functional/collections/GetMyDataCollectionItems.test.ts +++ b/test/functional/collections/GetMyDataCollectionItems.test.ts @@ -129,7 +129,7 @@ describe('execute', () => { } ]) expect(actual.countPerObjectType).toEqual({ - dataverses: 1, + collections: 1, datasets: 0, files: 0 }) diff --git a/test/integration/collections/CollectionsRepository.test.ts b/test/integration/collections/CollectionsRepository.test.ts index 13441469..1782dcd9 100644 --- a/test/integration/collections/CollectionsRepository.test.ts +++ b/test/integration/collections/CollectionsRepository.test.ts @@ -524,7 +524,7 @@ describe('CollectionsRepository', () => { expect(actualCollectionPreview.type).toBe(CollectionItemType.COLLECTION) expect(actual.totalItemCount).toBe(3) - expect(actual.countPerObjectType.dataverses).toBe(1) + expect(actual.countPerObjectType.collections).toBe(1) expect(actual.countPerObjectType.datasets).toBe(1) expect(actual.countPerObjectType.files).toBe(1) @@ -548,7 +548,7 @@ describe('CollectionsRepository', () => { ) expect(actual.totalItemCount).toBe(1) expect((actual.items[0] as FilePreview).name).toBe(expectedFileName) - expect(actual.countPerObjectType.dataverses).toBe(0) + expect(actual.countPerObjectType.collections).toBe(0) expect(actual.countPerObjectType.datasets).toBe(0) expect(actual.countPerObjectType.files).toBe(1) @@ -563,7 +563,7 @@ describe('CollectionsRepository', () => { ) expect(actual.totalItemCount).toBe(1) expect((actual.items[0] as DatasetPreview).title).toBe(expectedDatasetDescription) - expect(actual.countPerObjectType.dataverses).toBe(0) + expect(actual.countPerObjectType.collections).toBe(0) expect(actual.countPerObjectType.datasets).toBe(1) expect(actual.countPerObjectType.files).toBe(0) @@ -578,7 +578,7 @@ describe('CollectionsRepository', () => { expect(actual.totalItemCount).toBe(2) expect((actual.items[0] as DatasetPreview).title).toBe(expectedDatasetDescription) expect((actual.items[1] as CollectionPreview).name).toBe(expectedCollectionsName) - expect(actual.countPerObjectType.dataverses).toBe(1) + expect(actual.countPerObjectType.collections).toBe(1) expect(actual.countPerObjectType.datasets).toBe(1) expect(actual.countPerObjectType.files).toBe(0) @@ -592,7 +592,7 @@ describe('CollectionsRepository', () => { expect(actual.items.length).toBe(1) expect(actual.totalItemCount).toBe(2) expect((actual.items[0] as CollectionPreview).name).toBe(expectedCollectionsName) - expect(actual.countPerObjectType.dataverses).toBe(1) + expect(actual.countPerObjectType.collections).toBe(1) expect(actual.countPerObjectType.datasets).toBe(1) expect(actual.countPerObjectType.files).toBe(0) @@ -609,7 +609,7 @@ describe('CollectionsRepository', () => { expect(actual.totalItemCount).toBe(1) expect((actual.items[0] as CollectionPreview).name).toBe(expectedCollectionsName) expect(actual.facets).toEqual(expectedFacetsFromCollectionOnly) - expect(actual.countPerObjectType.dataverses).toBe(1) + expect(actual.countPerObjectType.collections).toBe(1) expect(actual.countPerObjectType.datasets).toBe(1) expect(actual.countPerObjectType.files).toBe(1) @@ -627,7 +627,7 @@ describe('CollectionsRepository', () => { expect(actual.totalItemCount).toBe(1) expect((actual.items[0] as DatasetPreview).title).toBe(expectedDatasetDescription) expect(actual.facets).toEqual(expectedFacetsFromDatasetOnly) - expect(actual.countPerObjectType.dataverses).toBe(1) + expect(actual.countPerObjectType.collections).toBe(1) expect(actual.countPerObjectType.datasets).toBe(1) expect(actual.countPerObjectType.files).toBe(1) @@ -645,7 +645,7 @@ describe('CollectionsRepository', () => { expect(actual.totalItemCount).toBe(1) expect((actual.items[0] as FilePreview).name).toBe(expectedFileName) expect(actual.facets).toEqual(expectedFacetsFromFileOnly) - expect(actual.countPerObjectType.dataverses).toBe(1) + expect(actual.countPerObjectType.collections).toBe(1) expect(actual.countPerObjectType.datasets).toBe(1) expect(actual.countPerObjectType.files).toBe(1) @@ -665,7 +665,7 @@ describe('CollectionsRepository', () => { expect((actual.items[0] as FilePreview).name).toBe(expectedFileName) expect((actual.items[1] as CollectionPreview).name).toBe(expectedCollectionsName) expect(actual.facets).toEqual(expectedFacetsFromCollectionAndFile) - expect(actual.countPerObjectType.dataverses).toBe(1) + expect(actual.countPerObjectType.collections).toBe(1) expect(actual.countPerObjectType.datasets).toBe(1) expect(actual.countPerObjectType.files).toBe(1) @@ -685,7 +685,7 @@ describe('CollectionsRepository', () => { expect((actual.items[0] as DatasetPreview).type).toBe(CollectionItemType.DATASET) expect((actual.items[1] as CollectionPreview).type).toBe(CollectionItemType.COLLECTION) expect((actual.items[2] as FilePreview).type).toBe(CollectionItemType.FILE) - expect(actual.countPerObjectType.dataverses).toBe(1) + expect(actual.countPerObjectType.collections).toBe(1) expect(actual.countPerObjectType.datasets).toBe(1) expect(actual.countPerObjectType.files).toBe(1) @@ -705,7 +705,7 @@ describe('CollectionsRepository', () => { expect((actual.items[0] as FilePreview).type).toBe(CollectionItemType.FILE) expect((actual.items[1] as CollectionPreview).type).toBe(CollectionItemType.COLLECTION) expect((actual.items[2] as DatasetPreview).type).toBe(CollectionItemType.DATASET) - expect(actual.countPerObjectType.dataverses).toBe(1) + expect(actual.countPerObjectType.collections).toBe(1) expect(actual.countPerObjectType.datasets).toBe(1) expect(actual.countPerObjectType.files).toBe(1) @@ -725,7 +725,7 @@ describe('CollectionsRepository', () => { expect((actual.items[0] as CollectionPreview).type).toBe(CollectionItemType.COLLECTION) expect((actual.items[1] as DatasetPreview).type).toBe(CollectionItemType.DATASET) expect((actual.items[2] as FilePreview).type).toBe(CollectionItemType.FILE) - expect(actual.countPerObjectType.dataverses).toBe(1) + expect(actual.countPerObjectType.collections).toBe(1) expect(actual.countPerObjectType.datasets).toBe(1) expect(actual.countPerObjectType.files).toBe(1) @@ -745,7 +745,7 @@ describe('CollectionsRepository', () => { expect((actual.items[0] as DatasetPreview).type).toBe(CollectionItemType.DATASET) expect((actual.items[1] as FilePreview).type).toBe(CollectionItemType.FILE) expect((actual.items[2] as CollectionPreview).type).toBe(CollectionItemType.COLLECTION) - expect(actual.countPerObjectType.dataverses).toBe(1) + expect(actual.countPerObjectType.collections).toBe(1) expect(actual.countPerObjectType.datasets).toBe(1) expect(actual.countPerObjectType.files).toBe(1) @@ -763,7 +763,7 @@ describe('CollectionsRepository', () => { expect(actual.totalItemCount).toBe(1) expect((actual.items[0] as CollectionPreview).name).toBe(expectedCollectionsName) expect(actual.facets).toEqual(expectedFacetsFromCollectionOnly) - expect(actual.countPerObjectType.dataverses).toBe(1) + expect(actual.countPerObjectType.collections).toBe(1) expect(actual.countPerObjectType.datasets).toBe(0) expect(actual.countPerObjectType.files).toBe(0) @@ -783,7 +783,7 @@ describe('CollectionsRepository', () => { expect(actual.totalItemCount).toBe(1) expect((actual.items[0] as DatasetPreview).title).toBe(expectedDatasetDescription) expect(actual.facets).toEqual(expectedFacetsFromDatasetOnly) - expect(actual.countPerObjectType.dataverses).toBe(0) + expect(actual.countPerObjectType.collections).toBe(0) expect(actual.countPerObjectType.datasets).toBe(1) expect(actual.countPerObjectType.files).toBe(0) @@ -802,7 +802,7 @@ describe('CollectionsRepository', () => { expect(actual.totalItemCount).toBe(1) expect((actual.items[0] as FilePreview).name).toBe(expectedFileName) expect(actual.facets).toEqual(expectedFacetsFromFileOnly) - expect(actual.countPerObjectType.dataverses).toBe(0) + expect(actual.countPerObjectType.collections).toBe(0) expect(actual.countPerObjectType.datasets).toBe(0) expect(actual.countPerObjectType.files).toBe(1) }) diff --git a/test/unit/collections/CollectionsRepository.test.ts b/test/unit/collections/CollectionsRepository.test.ts index d95075ed..e0092222 100644 --- a/test/unit/collections/CollectionsRepository.test.ts +++ b/test/unit/collections/CollectionsRepository.test.ts @@ -375,7 +375,7 @@ describe('CollectionsRepository', () => { const testTotalCount = 2 const testFacets = createCollectionItemsFacetsModel() const testCountPerObjectType = { - dataverses: 0, + collections: 0, datasets: 1, files: 1 } From 4c6215485abeecac976329409debfb49e7476b9c Mon Sep 17 00:00:00 2001 From: Ellen Kraffmiller Date: Mon, 28 Apr 2025 21:30:11 -0400 Subject: [PATCH 15/28] update countPerObjectType in unit tests --- test/unit/collections/GetCollectionItems.test.ts | 2 +- test/unit/collections/GetMyDataCollectionItems.test.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/test/unit/collections/GetCollectionItems.test.ts b/test/unit/collections/GetCollectionItems.test.ts index a1eb9936..cf2e7448 100644 --- a/test/unit/collections/GetCollectionItems.test.ts +++ b/test/unit/collections/GetCollectionItems.test.ts @@ -19,7 +19,7 @@ describe('execute', () => { const testTotalCount = 3 const testFacets = createCollectionItemsFacetsModel() const testCountPerObjectType = { - dataverses: 1, + collections: 1, datasets: 1, files: 1 } diff --git a/test/unit/collections/GetMyDataCollectionItems.test.ts b/test/unit/collections/GetMyDataCollectionItems.test.ts index f054e013..4a4d2c2f 100644 --- a/test/unit/collections/GetMyDataCollectionItems.test.ts +++ b/test/unit/collections/GetMyDataCollectionItems.test.ts @@ -49,7 +49,7 @@ describe('GetMyDataCollectionItems', () => { items: testItems, publishingFacet: testFacets, totalItemCount: 30, - countPerObjectType: { dataverses: 10, datasets: 15, files: 5 } + countPerObjectType: { collections: 10, datasets: 15, files: 5 } } beforeEach(() => { collectionRepositoryStub = {} as ICollectionsRepository From 35a85f37c31326b2373e534bcc1a1e59f8b84e08 Mon Sep 17 00:00:00 2001 From: Ellen Kraffmiller Date: Wed, 30 Apr 2025 10:30:44 -0400 Subject: [PATCH 16/28] revert wait time in PublishDataset.test.ts --- test/functional/datasets/PublishDataset.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/functional/datasets/PublishDataset.test.ts b/test/functional/datasets/PublishDataset.test.ts index f4b22be0..9bdbfdb6 100644 --- a/test/functional/datasets/PublishDataset.test.ts +++ b/test/functional/datasets/PublishDataset.test.ts @@ -67,7 +67,7 @@ describe('execute', () => { createdDatasetIdentifiers.persistentId, VersionUpdateType.MAJOR ) - await waitForNoLocks(createdDatasetIdentifiers.numericId, 30) + await waitForNoLocks(createdDatasetIdentifiers.numericId, 10) expect(response).toBeUndefined() await deletePublishedDatasetViaApi(createdDatasetIdentifiers.persistentId) From 0a0dd8889d38e089962117fce35fea4c0b50bb03 Mon Sep 17 00:00:00 2001 From: Ellen Kraffmiller Date: Wed, 30 Apr 2025 14:54:28 -0400 Subject: [PATCH 17/28] use CollectionItemSubset.ts as return type --- docs/useCases.md | 9 ++-- .../domain/models/CollectionItemSubset.ts | 6 --- .../repositories/ICollectionsRepository.ts | 4 +- .../useCases/GetMyDataCollectionItems.ts | 12 ++--- .../repositories/CollectionsRepository.ts | 7 +-- .../transformers/collectionTransformers.ts | 33 ++++++++------ .../GetMyDataCollectionItems.test.ts | 44 +++++++++++-------- .../GetMyDataCollectionItems.test.ts | 43 +++++++++--------- 8 files changed, 82 insertions(+), 76 deletions(-) diff --git a/docs/useCases.md b/docs/useCases.md index 4b7efff7..2250ee31 100644 --- a/docs/useCases.md +++ b/docs/useCases.md @@ -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) @@ -217,7 +218,7 @@ This use case supports the following optional parameters depending on the search #### List My Data Collection Items -Returns an instance of [MyDataCollectionItemSubset](../src/collections/domain/models/CollectionItemSubset.ts) that contains reduced information for each collection item for which the calling user has a role. +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: @@ -248,7 +249,7 @@ getMyDataCollectionItems searchText, otherUserName ) - .then((subset: MyDataCollectionItemSubset) => { + .then((subset: CollectionItemSubset) => { /* ... */ }) @@ -271,8 +272,8 @@ The `MyDataCollectionItemSubset`returned instance contains a property called `to This use case supports the following optional parameters depending on the search goals: -- **limit**: (number) Limit for pagination. -- **offset**: (number) Offset for pagination. +- **limit**: (number) Limit of items per page for pagination. (default is 10) +- **selectedPage**: (number) the page of results to be returned. (default is 1) #### Get Collection Featured Items diff --git a/src/collections/domain/models/CollectionItemSubset.ts b/src/collections/domain/models/CollectionItemSubset.ts index 16589588..59bb3fb5 100644 --- a/src/collections/domain/models/CollectionItemSubset.ts +++ b/src/collections/domain/models/CollectionItemSubset.ts @@ -8,12 +8,6 @@ export interface CollectionItemSubset { totalItemCount: number countPerObjectType: CountPerObjectType } -export interface MyDataCollectionItemSubset { - items: (CollectionPreview | DatasetPreview | FilePreview)[] - publishingFacet: CollectionItemsFacetLabel[] - totalItemCount: number - countPerObjectType: CountPerObjectType -} export interface CollectionItemsFacet { name: string diff --git a/src/collections/domain/repositories/ICollectionsRepository.ts b/src/collections/domain/repositories/ICollectionsRepository.ts index a2cbc045..c8fc6549 100644 --- a/src/collections/domain/repositories/ICollectionsRepository.ts +++ b/src/collections/domain/repositories/ICollectionsRepository.ts @@ -3,7 +3,7 @@ import { CollectionFeaturedItemsDTO } from '../dtos/CollectionFeaturedItemsDTO' import { Collection } from '../models/Collection' import { CollectionFacet } from '../models/CollectionFacet' import { CollectionFeaturedItem } from '../models/CollectionFeaturedItem' -import { CollectionItemSubset, MyDataCollectionItemSubset } from '../models/CollectionItemSubset' +import { CollectionItemSubset } from '../models/CollectionItemSubset' import { CollectionSearchCriteria } from '../models/CollectionSearchCriteria' import { CollectionUserPermissions } from '../models/CollectionUserPermissions' import { PublicationStatus } from '../../../../src/core/domain/models/PublicationStatus' @@ -35,7 +35,7 @@ export interface ICollectionsRepository { selectedPage?: number, searchText?: string, otherUserName?: string - ): Promise + ): Promise updateCollection( collectionIdOrAlias: number | string, updatedCollection: CollectionDTO diff --git a/src/collections/domain/useCases/GetMyDataCollectionItems.ts b/src/collections/domain/useCases/GetMyDataCollectionItems.ts index 76623f52..fea5add2 100644 --- a/src/collections/domain/useCases/GetMyDataCollectionItems.ts +++ b/src/collections/domain/useCases/GetMyDataCollectionItems.ts @@ -1,10 +1,10 @@ import { UseCase } from '../../../core/domain/useCases/UseCase' -import { MyDataCollectionItemSubset } from '../models/CollectionItemSubset' +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 { +export class GetMyDataCollectionItems implements UseCase { private collectionsRepository: ICollectionsRepository constructor(collectionsRepository: ICollectionsRepository) { @@ -18,7 +18,7 @@ export class GetMyDataCollectionItems implements UseCase} */ @@ -27,16 +27,16 @@ export class GetMyDataCollectionItems implements UseCase { + ): Promise { return await this.collectionsRepository.getMyDataCollectionItems( roleIds, collectionItemTypes, publicationStatuses, limit, - offset, + selectedPage, searchText, otherUserName ) diff --git a/src/collections/infra/repositories/CollectionsRepository.ts b/src/collections/infra/repositories/CollectionsRepository.ts index e6220347..aa9a5e59 100644 --- a/src/collections/infra/repositories/CollectionsRepository.ts +++ b/src/collections/infra/repositories/CollectionsRepository.ts @@ -11,10 +11,7 @@ import { CollectionDTO } from '../../domain/dtos/CollectionDTO' import { CollectionFacet } from '../../domain/models/CollectionFacet' import { CollectionUserPermissions } from '../../domain/models/CollectionUserPermissions' import { transformCollectionUserPermissionsResponseToCollectionUserPermissions } from './transformers/collectionUserPermissionsTransformers' -import { - CollectionItemSubset, - MyDataCollectionItemSubset -} from '../../domain/models/CollectionItemSubset' +import { CollectionItemSubset } from '../../domain/models/CollectionItemSubset' import { CollectionSearchCriteria, OrderType, @@ -212,7 +209,7 @@ export class CollectionsRepository extends ApiRepository implements ICollections selectedPage?: number, searchText?: string, userIdentifier?: string - ): Promise { + ): Promise { const queryParams = new URLSearchParams() if (limit) { diff --git a/src/collections/infra/repositories/transformers/collectionTransformers.ts b/src/collections/infra/repositories/transformers/collectionTransformers.ts index 185d2ac2..aed10e29 100644 --- a/src/collections/infra/repositories/transformers/collectionTransformers.ts +++ b/src/collections/infra/repositories/transformers/collectionTransformers.ts @@ -11,8 +11,7 @@ import { CollectionFacetPayload } from './CollectionFacetPayload' import { CollectionItemsFacet, CollectionItemsFacetLabel, - CollectionItemSubset, - MyDataCollectionItemSubset + CollectionItemSubset } from '../../../domain/models/CollectionItemSubset' import { DatasetPreview } from '../../../../datasets' import { FilePreview } from '../../../../files' @@ -150,7 +149,7 @@ export const transformCollectionItemsResponseToCollectionItemSubset = ( export const transformMyDataResponseToCollectionItemSubset = ( response: AxiosResponse -): MyDataCollectionItemSubset => { +): CollectionItemSubset => { const responseDataPayload = response.data.data const itemsPayload = responseDataPayload.items const countPerObjectTypePayload = responseDataPayload[ @@ -189,13 +188,14 @@ export const transformMyDataResponseToCollectionItemSubset = ( datasets: countPerObjectTypePayload['datasets_count'], files: countPerObjectTypePayload['files_count'] } - const publishingFacet: CollectionItemsFacetLabel[] = transformPublicationStatusResponseToLabels( + const publishingFacet: CollectionItemsFacet = transformPublicationStatusResponseToFacet( responseDataPayload.pubstatus_counts as MyDataPublicationStatusCountsPayload ) + const facets: CollectionItemsFacet[] = [publishingFacet] return { items, - publishingFacet, + facets, totalItemCount: responseDataPayload.pagination.numResults, countPerObjectType } @@ -210,14 +210,19 @@ const transformContactsPayloadToContacts = ( })) } -const transformPublicationStatusResponseToLabels = ( +const transformPublicationStatusResponseToFacet = ( publicationStatusCounts: MyDataPublicationStatusCountsPayload -): CollectionItemsFacetLabel[] => { - const labels: CollectionItemsFacetLabel[] = [] - labels.push({ name: 'Published', count: publicationStatusCounts.published_count }) - labels.push({ name: 'Unpublished', count: publicationStatusCounts.unpublished_count }) - labels.push({ name: 'Draft', count: publicationStatusCounts.draft_count }) - labels.push({ name: 'In Review', count: publicationStatusCounts.in_review_count }) - labels.push({ name: 'Deaccessioned', count: publicationStatusCounts.deaccessioned_count }) - return labels +): CollectionItemsFacet => { + const labels: CollectionItemsFacetLabel[] = [ + { name: 'Published', count: publicationStatusCounts.published_count }, + { name: 'Unpublished', count: publicationStatusCounts.unpublished_count }, + { name: 'Draft', count: publicationStatusCounts.draft_count }, + { name: 'In Review', count: publicationStatusCounts.in_review_count }, + { name: 'Deaccessioned', count: publicationStatusCounts.deaccessioned_count } + ] + return { + name: 'publicationStatus', + friendlyName: 'Publication Status', + labels + } } diff --git a/test/functional/collections/GetMyDataCollectionItems.test.ts b/test/functional/collections/GetMyDataCollectionItems.test.ts index a7e0e69b..0c303d5e 100644 --- a/test/functional/collections/GetMyDataCollectionItems.test.ts +++ b/test/functional/collections/GetMyDataCollectionItems.test.ts @@ -106,26 +106,32 @@ describe('execute', () => { expect(actualCollectionPreview.alias).toBe(testCollectionAlias) expect(actual.totalItemCount).toBe(1) - expect(actual.publishingFacet).toEqual([ + expect(actual.facets).toEqual([ { - name: 'Published', - count: 0 - }, - { - name: 'Unpublished', - count: 1 - }, - { - name: 'Draft', - count: 0 - }, - { - name: 'In Review', - count: 0 - }, - { - name: 'Deaccessioned', - count: 0 + name: 'publicationStatus', + friendlyName: 'Publication Status', + labels: [ + { + name: 'Published', + count: 0 + }, + { + name: 'Unpublished', + count: 1 + }, + { + name: 'Draft', + count: 0 + }, + { + name: 'In Review', + count: 0 + }, + { + name: 'Deaccessioned', + count: 0 + } + ] } ]) expect(actual.countPerObjectType).toEqual({ diff --git a/test/unit/collections/GetMyDataCollectionItems.test.ts b/test/unit/collections/GetMyDataCollectionItems.test.ts index 4a4d2c2f..edbb3e04 100644 --- a/test/unit/collections/GetMyDataCollectionItems.test.ts +++ b/test/unit/collections/GetMyDataCollectionItems.test.ts @@ -5,12 +5,9 @@ import { CollectionItemType } from '../../../src/collections/domain/models/Colle import { createDatasetPreviewModel } from '../../testHelpers/datasets/datasetPreviewHelper' import { createFilePreviewModel } from '../../testHelpers/files/filePreviewHelper' import { createCollectionPreviewModel } from '../../testHelpers/collections/collectionPreviewHelper' -import { - CollectionItemsFacetLabel, - MyDataCollectionItemSubset -} from '../../../src/collections/domain/models/CollectionItemSubset' +import { CollectionItemsFacet } from '../../../src/collections/domain/models/CollectionItemSubset' import { PublicationStatus } from '../../../src/core/domain/models/PublicationStatus' - +import { CollectionItemSubset } from '../../../src/collections/domain/models/CollectionItemSubset' describe('GetMyDataCollectionItems', () => { let collectionRepositoryStub: ICollectionsRepository let testGetMyDataCollectionItems: GetMyDataCollectionItems @@ -27,27 +24,33 @@ describe('GetMyDataCollectionItems', () => { const testSearchText = 'test' const testOtherUserName = 'testUser' const testItems = [ + createCollectionPreviewModel(), createDatasetPreviewModel(), - createFilePreviewModel(), - createCollectionPreviewModel() + createFilePreviewModel() ] const testFacets = [ { - name: 'Published', - count: 10 - }, - { - name: 'Draft', - count: 5 - }, - { - name: 'Unpublished', - count: 15 + name: 'publicationStatus', + friendlyName: 'Publication Status', + labels: [ + { + name: 'Published', + count: 10 + }, + { + name: 'Draft', + count: 5 + }, + { + name: 'Unpublished', + count: 15 + } + ] } - ] as CollectionItemsFacetLabel[] - const testItemSubset: MyDataCollectionItemSubset = { + ] as CollectionItemsFacet[] + const testItemSubset: CollectionItemSubset = { items: testItems, - publishingFacet: testFacets, + facets: testFacets, totalItemCount: 30, countPerObjectType: { collections: 10, datasets: 15, files: 5 } } From a153656e4fd45f96f6e945a4f6eb93dbb3f53761 Mon Sep 17 00:00:00 2001 From: Ellen Kraffmiller Date: Wed, 30 Apr 2025 17:08:46 -0400 Subject: [PATCH 18/28] add GetMyDataCollectionItems integration test --- .../collections/CollectionsRepository.test.ts | 330 +++++++++++++++++- 1 file changed, 329 insertions(+), 1 deletion(-) diff --git a/test/integration/collections/CollectionsRepository.test.ts b/test/integration/collections/CollectionsRepository.test.ts index 1782dcd9..ab3a6964 100644 --- a/test/integration/collections/CollectionsRepository.test.ts +++ b/test/integration/collections/CollectionsRepository.test.ts @@ -41,6 +41,7 @@ import { deleteCollectionFeaturedItemsViaApi, deleteCollectionFeaturedItemViaApi } from '../../testHelpers/collections/collectionFeaturedItemsHelper' +import { CollectionItemsFacet } from '../../../dist/collections/domain/models/CollectionItemSubset' describe('CollectionsRepository', () => { const testCollectionAlias = 'collectionsRepositoryTestCollection' @@ -350,7 +351,6 @@ describe('CollectionsRepository', () => { ) }) }) - describe('getCollectionItems', () => { let testDatasetIds: CreatedDatasetIdentifiers @@ -818,6 +818,334 @@ describe('CollectionsRepository', () => { }) }) + describe('getMyDataCollectionItems', () => { + let testDatasetIds: CreatedDatasetIdentifiers + + const testTextFile1Name = 'test-file-1.txt' + const testSubCollectionAlias = 'collectionsRepositoryMyDataCollection' + beforeAll(async () => { + await createCollectionViaApi(testSubCollectionAlias, testCollectionAlias).catch(() => { + throw new Error( + `Tests beforeAll(): Error while creating subcollection ${testSubCollectionAlias}` + ) + }) + try { + testDatasetIds = await createDataset.execute( + TestConstants.TEST_NEW_DATASET_DTO, + testSubCollectionAlias + ) + } catch (error) { + throw new Error('Tests beforeAll(): Error while creating test dataset') + } + await uploadFileViaApi(testDatasetIds.numericId, testTextFile1Name).catch(() => { + throw new Error(`Tests beforeAll(): Error while uploading file ${testTextFile1Name}`) + }) + }) + + afterAll(async () => { + try { + await deleteUnpublishedDatasetViaApi(testDatasetIds.numericId) + } catch (error) { + throw new Error( + `Tests afterAll(): Error while deleting test dataset ${testDatasetIds.numericId}` + ) + } + try { + await deleteCollectionViaApi(testSubCollectionAlias) + } catch (error) { + throw new Error( + `Tests afterAll(): Error while deleting subcollection ${testSubCollectionAlias}` + ) + } + }) + + test('should return collection items given valid roleIds', async () => { + // Give enough time to Solr for indexing + await new Promise((resolve) => setTimeout(resolve, 15000)) + // TODO: replace this with API call to get the role ids + const roleIds = [1, 2, 3, 4, 5, 6, 7, 8] + const publicationStatuses = [PublicationStatus.Draft, PublicationStatus.Unpublished] + const collectionItemTypes = [ + CollectionItemType.COLLECTION, + CollectionItemType.DATASET, + CollectionItemType.FILE + ] + let actual = await sut.getMyDataCollectionItems( + roleIds, + collectionItemTypes, + publicationStatuses + ) + const actualFilePreview = actual.items[1] as FilePreview + const actualDatasetPreview = actual.items[0] as DatasetPreview + const actualCollectionPreview = actual.items[2] as CollectionPreview + + const expectedFileMd5 = '68b22040025784da775f55cfcb6dee2e' + const expectedDatasetCitationFragment = `Admin, Dataverse; Owner, Dataverse, ${currentYear}, "Dataset created using the createDataset use case"` + const expectedDatasetDescription = 'Dataset created using the createDataset use case' + const expectedFileName = 'test-file-1.txt' + const expectedCollectionsName = 'Scientific Research' + + //prettier-ignore + const expectedFacetsAll = [ + { + name: 'publicationStatus', friendlyName: 'Publication Status', labels: [{ name: 'Unpublished', count: 3 },{ name: 'Draft', count: 2 }] + } + ] + expect(actualFilePreview.checksum?.type).toBe('MD5') + expect(actualFilePreview.checksum?.value).toBe(expectedFileMd5) + expect(actualFilePreview.datasetCitation).toContain(expectedDatasetCitationFragment) + expect(actualFilePreview.datasetId).toBe(testDatasetIds.numericId) + expect(actualFilePreview.datasetName).toBe(expectedDatasetDescription) + expect(actualFilePreview.datasetPersistentId).toBe(testDatasetIds.persistentId) + expect(actualFilePreview.description).toBe('') + expect(actualFilePreview.fileContentType).toBe('text/plain') + expect(actualFilePreview.fileId).not.toBeUndefined() + expect(actualFilePreview.fileType).toBe('Plain Text') + expect(actualFilePreview.md5).toBe(expectedFileMd5) + expect(actualFilePreview.name).toBe(expectedFileName) + expect(actualFilePreview.publicationStatuses[0]).toBe(PublicationStatus.Unpublished) + expect(actualFilePreview.publicationStatuses[1]).toBe(PublicationStatus.Draft) + expect(actualFilePreview.sizeInBytes).toBe(12) + expect(actualFilePreview.url).not.toBeUndefined() + expect(actualFilePreview.releaseOrCreateDate).not.toBeUndefined() + expect(actualFilePreview.type).toBe(CollectionItemType.FILE) + expect(actualFilePreview.restricted).toBe(false) + expect(actualFilePreview.canDownloadFile).toBe(true) + + expect(actualDatasetPreview.title).toBe(expectedDatasetDescription) + expect(actualDatasetPreview.citation).toContain(expectedDatasetCitationFragment) + expect(actualDatasetPreview.description).toBe('This is the description of the dataset.') + expect(actualDatasetPreview.persistentId).not.toBeUndefined() + expect(actualDatasetPreview.persistentId).not.toBeUndefined() + expect(actualDatasetPreview.publicationStatuses[0]).toBe(PublicationStatus.Unpublished) + expect(actualDatasetPreview.publicationStatuses[1]).toBe(PublicationStatus.Draft) + expect(actualDatasetPreview.versionId).not.toBeUndefined() + expect(actualDatasetPreview.versionInfo.createTime).not.toBeUndefined() + expect(actualDatasetPreview.versionInfo.lastUpdateTime).not.toBeUndefined() + expect(actualDatasetPreview.versionInfo.majorNumber).toBeUndefined() + expect(actualDatasetPreview.versionInfo.minorNumber).toBeUndefined() + expect(actualDatasetPreview.versionInfo.state).toBe('DRAFT') + expect(actualDatasetPreview.parentCollectionAlias).toBe( + 'collectionsRepositoryMyDataCollection' + ) + expect(actualDatasetPreview.parentCollectionName).toBe(expectedCollectionsName) + expect(actualDatasetPreview.type).toBe(CollectionItemType.DATASET) + + expect(actualCollectionPreview.name).toBe(expectedCollectionsName) + expect(actualCollectionPreview.alias).toBe(testSubCollectionAlias) + expect(actualCollectionPreview.description).toBe('We do all the science.') + expect(actualCollectionPreview.imageUrl).toBe(undefined) + expect(actualCollectionPreview.parentAlias).toBe(testCollectionAlias) + expect(actualCollectionPreview.parentName).toBe(expectedCollectionsName) + expect(actualCollectionPreview.publicationStatuses[0]).toBe(PublicationStatus.Unpublished) + expect(actualCollectionPreview.releaseOrCreateDate).not.toBeUndefined() + expect(actualCollectionPreview.affiliation).toBe('Scientific Research University') + expect(actualCollectionPreview.parentAlias).toBe('collectionsRepositoryTestCollection') + expect(actualCollectionPreview.parentName).toBe(expectedCollectionsName) + expect(actualCollectionPreview.type).toBe(CollectionItemType.COLLECTION) + expect(actual.items.length).toBe(3) + expect(actual.totalItemCount).toBe(3) + expect(actual.countPerObjectType.collections).toBe(1) + expect(actual.countPerObjectType.datasets).toBe(1) + expect(actual.countPerObjectType.files).toBe(1) + + expect(actual.facets).toEqual(expectedFacetsAll) + + // Test limit and selectedPage + actual = await sut.getMyDataCollectionItems( + roleIds, + collectionItemTypes, + publicationStatuses, + 1, + 1 + ) + expect((actual.items[0] as FilePreview).name).toBe(expectedFileName) + expect(actual.items.length).toBe(1) + expect(actual.totalItemCount).toBe(3) + + // Test search text + const fileNameSearchText = 'test-fi' + + actual = await sut.getMyDataCollectionItems( + roleIds, + collectionItemTypes, + publicationStatuses, + undefined, + undefined, + fileNameSearchText + ) + expect(actual.totalItemCount).toBe(1) + expect((actual.items[0] as FilePreview).name).toBe(expectedFileName) + expect(actual.countPerObjectType.collections).toBe(0) + expect(actual.countPerObjectType.datasets).toBe(0) + expect(actual.countPerObjectType.files).toBe(1) + + const datasetSearchText = 'This is the description' + + actual = await sut.getMyDataCollectionItems( + roleIds, + collectionItemTypes, + publicationStatuses, + undefined, + undefined, + datasetSearchText + ) + expect(actual.totalItemCount).toBe(1) + expect((actual.items[0] as DatasetPreview).title).toBe(expectedDatasetDescription) + expect(actual.countPerObjectType.collections).toBe(0) + expect(actual.countPerObjectType.datasets).toBe(1) + expect(actual.countPerObjectType.files).toBe(0) + + const searchTextForDatasetAndCollection = 'the' + actual = await sut.getMyDataCollectionItems( + roleIds, + collectionItemTypes, + publicationStatuses, + undefined, + undefined, + searchTextForDatasetAndCollection + ) + expect(actual.totalItemCount).toBe(2) + expect((actual.items[0] as DatasetPreview).title).toBe(expectedDatasetDescription) + expect((actual.items[1] as CollectionPreview).name).toBe(expectedCollectionsName) + expect(actual.facets as CollectionItemsFacet[]).toBe({ + name: 'publicationStatus', + friendlyName: 'Publication Status', + labels: [ + { name: 'Unpublished', count: 1 }, + { name: 'Draft', count: 1 } + ] + }) + expect(actual.countPerObjectType.collections).toBe(1) + expect(actual.countPerObjectType.datasets).toBe(1) + expect(actual.countPerObjectType.files).toBe(0) + + // Test search text, limit and offset + // TODO: run this test when the limit param has been fixed in the Dataverse API + /* + actual = await sut.getMyDataCollectionItems( + roleIds, + collectionItemTypes, + publicationStatuses, + 1, + 1, + searchTextForDatasetAndCollection + ) + expect(actual.items.length).toBe(1) + expect(actual.totalItemCount).toBe(2) + expect((actual.items[0] as CollectionPreview).name).toBe(expectedCollectionsName) + expect(actual.countPerObjectType.collections).toBe(1) + expect(actual.countPerObjectType.datasets).toBe(1) + expect(actual.countPerObjectType.files).toBe(0) + */ + + // Test type collection + const searchForCollectionType = [CollectionItemType.COLLECTION] + actual = await sut.getMyDataCollectionItems( + roleIds, + searchForCollectionType, + publicationStatuses, + undefined, + undefined + ) + expect(actual.items.length).toBe(1) + expect(actual.totalItemCount).toBe(1) + expect((actual.items[0] as CollectionPreview).name).toBe(expectedCollectionsName) + expect(actual.facets).toEqual([ + { + name: 'publicationStatus', + friendlyName: 'Publication Status', + labels: [{ name: 'Unpublished', count: 1 }] + } + ]) + expect(actual.countPerObjectType.collections).toBe(1) + expect(actual.countPerObjectType.datasets).toBe(1) + expect(actual.countPerObjectType.files).toBe(1) + + // Test type dataset + const collectionSearchCriteriaForDatasetType = new CollectionSearchCriteria().withItemTypes([ + CollectionItemType.DATASET + ]) + actual = await sut.getCollectionItems( + testCollectionAlias, + undefined, + undefined, + collectionSearchCriteriaForDatasetType + ) + expect(actual.items.length).toBe(1) + expect(actual.totalItemCount).toBe(1) + expect((actual.items[0] as DatasetPreview).title).toBe(expectedDatasetDescription) + expect(actual.facets).toEqual([ + { + name: 'publicationStatus', + friendlyName: 'Publication Status', + labels: [ + { name: 'Draft', count: 1 }, + { name: 'Unpublished', count: 1 } + ] + } + ]) + expect(actual.countPerObjectType.collections).toBe(1) + expect(actual.countPerObjectType.datasets).toBe(1) + expect(actual.countPerObjectType.files).toBe(1) + + // Test type file + const collectionSearchCriteriaForFileType = new CollectionSearchCriteria().withItemTypes([ + CollectionItemType.FILE + ]) + actual = await sut.getCollectionItems( + testCollectionAlias, + undefined, + undefined, + collectionSearchCriteriaForFileType + ) + expect(actual.items.length).toBe(1) + expect(actual.totalItemCount).toBe(1) + expect((actual.items[0] as FilePreview).name).toBe(expectedFileName) + expect(actual.facets).toEqual([ + { + name: 'publicationStatus', + friendlyName: 'Publication Status', + labels: [ + { name: 'Draft', count: 1 }, + { name: 'Unpublished', count: 1 } + ] + } + ]) + + expect(actual.countPerObjectType.collections).toBe(1) + expect(actual.countPerObjectType.datasets).toBe(1) + expect(actual.countPerObjectType.files).toBe(1) + + // Test multiple types + const collectionSearchCriteriaForMultiTypes = new CollectionSearchCriteria().withItemTypes([ + CollectionItemType.FILE, + CollectionItemType.COLLECTION + ]) + actual = await sut.getCollectionItems( + testCollectionAlias, + undefined, + undefined, + collectionSearchCriteriaForMultiTypes + ) + expect(actual.items.length).toBe(2) + expect(actual.totalItemCount).toBe(2) + expect((actual.items[0] as FilePreview).name).toBe(expectedFileName) + expect((actual.items[1] as CollectionPreview).name).toBe(expectedCollectionsName) + expect(actual.countPerObjectType.collections).toBe(1) + expect(actual.countPerObjectType.datasets).toBe(1) + expect(actual.countPerObjectType.files).toBe(1) + }) + + test('should return error when role, type and publication status params are empty', async () => { + const expectedError = new ReadError('No results. Please select at least one Role.') + + await expect( + sut.getMyDataCollectionItems([], [], [], 0, 0, undefined, undefined) + ).rejects.toThrow(expectedError) + }) + }) + describe('getCollectionItems for published tabular file', () => { let testDatasetIds: CreatedDatasetIdentifiers const testTextFile4Name = 'test-file-4.tab' From 787735821d7af24336ff460ca6be6b809338c057 Mon Sep 17 00:00:00 2001 From: Ellen Kraffmiller Date: Wed, 30 Apr 2025 21:06:53 -0400 Subject: [PATCH 19/28] fix import --- test/integration/collections/CollectionsRepository.test.ts | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/test/integration/collections/CollectionsRepository.test.ts b/test/integration/collections/CollectionsRepository.test.ts index ab3a6964..7d351d6e 100644 --- a/test/integration/collections/CollectionsRepository.test.ts +++ b/test/integration/collections/CollectionsRepository.test.ts @@ -41,8 +41,7 @@ import { deleteCollectionFeaturedItemsViaApi, deleteCollectionFeaturedItemViaApi } from '../../testHelpers/collections/collectionFeaturedItemsHelper' -import { CollectionItemsFacet } from '../../../dist/collections/domain/models/CollectionItemSubset' - +import { CollectionItemsFacet } from '../../../src/collections/domain/models/CollectionItemSubset' describe('CollectionsRepository', () => { const testCollectionAlias = 'collectionsRepositoryTestCollection' const sut: CollectionsRepository = new CollectionsRepository() @@ -845,6 +844,7 @@ describe('CollectionsRepository', () => { afterAll(async () => { try { await deleteUnpublishedDatasetViaApi(testDatasetIds.numericId) + console.log('Deleted dataset') } catch (error) { throw new Error( `Tests afterAll(): Error while deleting test dataset ${testDatasetIds.numericId}` @@ -852,6 +852,7 @@ describe('CollectionsRepository', () => { } try { await deleteCollectionViaApi(testSubCollectionAlias) + console.log('Deleted collection') } catch (error) { throw new Error( `Tests afterAll(): Error while deleting subcollection ${testSubCollectionAlias}` @@ -861,7 +862,7 @@ describe('CollectionsRepository', () => { test('should return collection items given valid roleIds', async () => { // Give enough time to Solr for indexing - await new Promise((resolve) => setTimeout(resolve, 15000)) + await new Promise((resolve) => setTimeout(resolve, 5000)) // TODO: replace this with API call to get the role ids const roleIds = [1, 2, 3, 4, 5, 6, 7, 8] const publicationStatuses = [PublicationStatus.Draft, PublicationStatus.Unpublished] From 4a451844e8db61f4ec1f72dfdf19f2f31e029648 Mon Sep 17 00:00:00 2001 From: Ellen Kraffmiller Date: Thu, 1 May 2025 08:02:01 -0400 Subject: [PATCH 20/28] fix integration tests --- .../collections/CollectionsRepository.test.ts | 793 +++++++++--------- 1 file changed, 409 insertions(+), 384 deletions(-) diff --git a/test/integration/collections/CollectionsRepository.test.ts b/test/integration/collections/CollectionsRepository.test.ts index 7d351d6e..6d885d7a 100644 --- a/test/integration/collections/CollectionsRepository.test.ts +++ b/test/integration/collections/CollectionsRepository.test.ts @@ -155,6 +155,7 @@ describe('CollectionsRepository', () => { const actualAfterDatasetDeletion = await sut.getCollection(parentCollectionAlias) expect(actualAfterDatasetDeletion.childCount).toBe(0) + await deleteCollectionViaApi(parentCollectionAlias) }) }) @@ -350,12 +351,11 @@ describe('CollectionsRepository', () => { ) }) }) - describe('getCollectionItems', () => { + describe('getMyDataCollectionItems', () => { let testDatasetIds: CreatedDatasetIdentifiers - const testTextFile1Name = 'test-file-1.txt' - const testSubCollectionAlias = 'collectionsRepositoryTestSubCollection' - + const testTextFile1Name = 'test-file-2.txt' + const testSubCollectionAlias = 'collectionsRepositoryMyDataCollection' beforeAll(async () => { await createCollectionViaApi(testSubCollectionAlias, testCollectionAlias).catch(() => { throw new Error( @@ -378,6 +378,7 @@ describe('CollectionsRepository', () => { afterAll(async () => { try { await deleteUnpublishedDatasetViaApi(testDatasetIds.numericId) + console.log('Deleted dataset') } catch (error) { throw new Error( `Tests afterAll(): Error while deleting test dataset ${testDatasetIds.numericId}` @@ -385,6 +386,7 @@ describe('CollectionsRepository', () => { } try { await deleteCollectionViaApi(testSubCollectionAlias) + console.log('Deleted collection') } catch (error) { throw new Error( `Tests afterAll(): Error while deleting subcollection ${testSubCollectionAlias}` @@ -392,83 +394,45 @@ describe('CollectionsRepository', () => { } }) - test('should return collection items given a valid collection alias', async () => { + test('should return collection items given valid roleIds', async () => { // Give enough time to Solr for indexing await new Promise((resolve) => setTimeout(resolve, 5000)) - - let actual = await sut.getCollectionItems(testCollectionAlias) + // TODO: replace this with API call to get the role ids + const roleIds = [1, 2, 3, 4, 5, 6, 7, 8] + const publicationStatuses = [PublicationStatus.Draft, PublicationStatus.Unpublished] + const collectionItemTypes = [ + CollectionItemType.COLLECTION, + CollectionItemType.DATASET, + CollectionItemType.FILE + ] + let actual = await sut.getMyDataCollectionItems( + roleIds, + collectionItemTypes, + publicationStatuses + ) const actualFilePreview = actual.items[1] as FilePreview const actualDatasetPreview = actual.items[0] as DatasetPreview const actualCollectionPreview = actual.items[2] as CollectionPreview - const expectedFileMd5 = '68b22040025784da775f55cfcb6dee2e' + const expectedFileMd5 = '799b5c8c5fdcfbd56c3943f7a6c35326' const expectedDatasetCitationFragment = `Admin, Dataverse; Owner, Dataverse, ${currentYear}, "Dataset created using the createDataset use case"` const expectedDatasetDescription = 'Dataset created using the createDataset use case' - const expectedFileName = 'test-file-1.txt' + const expectedFileName = 'test-file-2.txt' const expectedCollectionsName = 'Scientific Research' - //prettier-ignore const expectedFacetsAll = [ { - name: 'dvCategory', friendlyName: 'Dataverse Category', labels: [{ name: 'Laboratory', count: 1 }] - }, - { - name: 'publicationStatus', friendlyName: 'Publication Status', labels: [{ name: 'Unpublished', count: 3 },{ name: 'Draft', count: 2 }] - }, - { - name: 'authorName_ss', friendlyName: 'Author Name', labels: [{ name: 'Admin, Dataverse', count: 1 },{ name: 'Owner, Dataverse', count: 1 }] - }, - { - name: 'subject_ss', friendlyName: 'Subject', labels: [{ name: 'Medicine, Health and Life Sciences', count: 1 }] - }, - { - name: 'fileTypeGroupFacet', friendlyName: 'File Type', labels: [{ name: 'Text', count: 1 }] - }, - { - name: 'fileAccess', friendlyName: 'Access', labels: [{ name: 'Public', count: 1 }] - } - ] - //prettier-ignore - const expectedFacetsFromCollectionOnly = [ - { - name: 'dvCategory', friendlyName: 'Dataverse Category', labels: [{ name: 'Laboratory', count: 1 }] - }, - { - name: 'publicationStatus', friendlyName: 'Publication Status', labels: [{ name: 'Unpublished', count: 1 }] - } - ] - //prettier-ignore - const expectedFacetsFromDatasetOnly = [ - { - name: 'publicationStatus', friendlyName: 'Publication Status', labels: [{ name: 'Draft', count: 1 },{ name: 'Unpublished', count: 1 }] - }, - { - name: 'authorName_ss', friendlyName: 'Author Name', labels: [{ name: 'Admin, Dataverse', count: 1 },{ name: 'Owner, Dataverse', count: 1 }] - }, - { - name: 'subject_ss', friendlyName: 'Subject', labels: [{ name: 'Medicine, Health and Life Sciences', count: 1 }] + name: 'publicationStatus', + friendlyName: 'Publication Status', + labels: [ + { name: 'Published', count: 0 }, + { name: 'Unpublished', count: 4 }, + { name: 'Draft', count: 2 }, + { name: 'In Review', count: 0 }, + { name: 'Deaccessioned', count: 0 } + ] } ] - //prettier-ignore - const expectedFacetsFromFileOnly = [ - { - name: 'publicationStatus', friendlyName: 'Publication Status', labels: [{ name: 'Draft', count: 1 },{ name: 'Unpublished', count: 1 }] - }, - { name: 'fileTypeGroupFacet', friendlyName: 'File Type', labels: [{ name: 'Text', count: 1 }] }, - { name: 'fileAccess', friendlyName: 'Access', labels: [{ name: 'Public', count: 1 }] } - ] - //prettier-ignore - const expectedFacetsFromCollectionAndFile = [ - { - name: 'dvCategory', friendlyName: 'Dataverse Category', labels: [{ name: 'Laboratory', count: 1 }] - }, - { - name: 'publicationStatus', friendlyName: 'Publication Status', labels: [{ name: 'Unpublished', count: 2 },{ name: 'Draft', count: 1 }] - }, - { name: 'fileTypeGroupFacet', friendlyName: 'File Type', labels: [{ name: 'Text', count: 1 }] }, - { name: 'fileAccess', friendlyName: 'Access', labels: [{ name: 'Public', count: 1 }] } - ] - expect(actualFilePreview.checksum?.type).toBe('MD5') expect(actualFilePreview.checksum?.value).toBe(expectedFileMd5) expect(actualFilePreview.datasetCitation).toContain(expectedDatasetCitationFragment) @@ -504,7 +468,7 @@ describe('CollectionsRepository', () => { expect(actualDatasetPreview.versionInfo.minorNumber).toBeUndefined() expect(actualDatasetPreview.versionInfo.state).toBe('DRAFT') expect(actualDatasetPreview.parentCollectionAlias).toBe( - 'collectionsRepositoryTestSubCollection' + 'collectionsRepositoryMyDataCollection' ) expect(actualDatasetPreview.parentCollectionName).toBe(expectedCollectionsName) expect(actualDatasetPreview.type).toBe(CollectionItemType.DATASET) @@ -521,29 +485,37 @@ describe('CollectionsRepository', () => { expect(actualCollectionPreview.parentAlias).toBe('collectionsRepositoryTestCollection') expect(actualCollectionPreview.parentName).toBe(expectedCollectionsName) expect(actualCollectionPreview.type).toBe(CollectionItemType.COLLECTION) - - expect(actual.totalItemCount).toBe(3) - expect(actual.countPerObjectType.collections).toBe(1) + console.log('actual.items', actual.items) + expect(actual.items.length).toBe(4) + expect(actual.totalItemCount).toBe(4) + expect(actual.countPerObjectType.collections).toBe(2) expect(actual.countPerObjectType.datasets).toBe(1) expect(actual.countPerObjectType.files).toBe(1) expect(actual.facets).toEqual(expectedFacetsAll) - // Test limit and offset - actual = await sut.getCollectionItems(testCollectionAlias, 1, 1) - expect((actual.items[0] as FilePreview).name).toBe(expectedFileName) - expect(actual.items.length).toBe(1) - expect(actual.totalItemCount).toBe(3) + // Test limit and selectedPage + actual = await sut.getMyDataCollectionItems( + roleIds, + collectionItemTypes, + publicationStatuses, + 1, + 1 + ) + expect((actual.items[1] as FilePreview).name).toBe(expectedFileName) + expect(actual.items.length).toBe(4) + expect(actual.totalItemCount).toBe(4) // Test search text - const collectionSearchCriteriaForFile = new CollectionSearchCriteria().withSearchText( - 'test-fi' - ) - actual = await sut.getCollectionItems( - testCollectionAlias, + const fileNameSearchText = 'test-fi' + + actual = await sut.getMyDataCollectionItems( + roleIds, + collectionItemTypes, + publicationStatuses, undefined, undefined, - collectionSearchCriteriaForFile + fileNameSearchText ) expect(actual.totalItemCount).toBe(1) expect((actual.items[0] as FilePreview).name).toBe(expectedFileName) @@ -551,14 +523,15 @@ describe('CollectionsRepository', () => { expect(actual.countPerObjectType.datasets).toBe(0) expect(actual.countPerObjectType.files).toBe(1) - const collectionSearchCriteriaForDataset = new CollectionSearchCriteria().withSearchText( - 'This is the description' - ) - actual = await sut.getCollectionItems( - testCollectionAlias, + const datasetSearchText = 'This is the description' + + actual = await sut.getMyDataCollectionItems( + roleIds, + collectionItemTypes, + publicationStatuses, undefined, undefined, - collectionSearchCriteriaForDataset + datasetSearchText ) expect(actual.totalItemCount).toBe(1) expect((actual.items[0] as DatasetPreview).title).toBe(expectedDatasetDescription) @@ -566,27 +539,47 @@ describe('CollectionsRepository', () => { expect(actual.countPerObjectType.datasets).toBe(1) expect(actual.countPerObjectType.files).toBe(0) - const collectionSearchCriteriaForDatasetAndCollection = - new CollectionSearchCriteria().withSearchText('the') - actual = await sut.getCollectionItems( - testCollectionAlias, + const searchTextForDatasetAndCollection = 'the' + actual = await sut.getMyDataCollectionItems( + roleIds, + collectionItemTypes, + publicationStatuses, undefined, undefined, - collectionSearchCriteriaForDatasetAndCollection + searchTextForDatasetAndCollection ) - expect(actual.totalItemCount).toBe(2) + expect(actual.totalItemCount).toBe(3) expect((actual.items[0] as DatasetPreview).title).toBe(expectedDatasetDescription) expect((actual.items[1] as CollectionPreview).name).toBe(expectedCollectionsName) - expect(actual.countPerObjectType.collections).toBe(1) + expect((actual.items[2] as CollectionPreview).name).toBe(expectedCollectionsName) + console.log('actual.facets', actual.facets) + expect(actual.facets as CollectionItemsFacet[]).toEqual([ + { + name: 'publicationStatus', + friendlyName: 'Publication Status', + labels: [ + { name: 'Published', count: 0 }, + { name: 'Unpublished', count: 3 }, + { name: 'Draft', count: 1 }, + { name: 'In Review', count: 0 }, + { name: 'Deaccessioned', count: 0 } + ] + } + ]) + expect(actual.countPerObjectType.collections).toBe(2) expect(actual.countPerObjectType.datasets).toBe(1) expect(actual.countPerObjectType.files).toBe(0) // Test search text, limit and offset - actual = await sut.getCollectionItems( - testCollectionAlias, + // TODO: run this test when the limit param has been fixed in the Dataverse API + /* + actual = await sut.getMyDataCollectionItems( + roleIds, + collectionItemTypes, + publicationStatuses, 1, 1, - collectionSearchCriteriaForDatasetAndCollection + searchTextForDatasetAndCollection ) expect(actual.items.length).toBe(1) expect(actual.totalItemCount).toBe(2) @@ -594,234 +587,130 @@ describe('CollectionsRepository', () => { expect(actual.countPerObjectType.collections).toBe(1) expect(actual.countPerObjectType.datasets).toBe(1) expect(actual.countPerObjectType.files).toBe(0) + */ // Test type collection - const collectionSearchCriteriaForCollectionType = - new CollectionSearchCriteria().withItemTypes([CollectionItemType.COLLECTION]) - actual = await sut.getCollectionItems( - testCollectionAlias, - undefined, + const searchForCollectionType = [CollectionItemType.COLLECTION] + actual = await sut.getMyDataCollectionItems( + roleIds, + searchForCollectionType, + publicationStatuses, undefined, - collectionSearchCriteriaForCollectionType + undefined ) - expect(actual.items.length).toBe(1) - expect(actual.totalItemCount).toBe(1) + expect(actual.items.length).toBe(2) + expect(actual.totalItemCount).toBe(2) expect((actual.items[0] as CollectionPreview).name).toBe(expectedCollectionsName) - expect(actual.facets).toEqual(expectedFacetsFromCollectionOnly) - expect(actual.countPerObjectType.collections).toBe(1) - expect(actual.countPerObjectType.datasets).toBe(1) - expect(actual.countPerObjectType.files).toBe(1) + expect(actual.facets).toEqual([ + { + name: 'publicationStatus', + friendlyName: 'Publication Status', + labels: [ + { name: 'Published', count: 0 }, + { name: 'Unpublished', count: 2 }, + { name: 'Draft', count: 0 }, + { name: 'In Review', count: 0 }, + { name: 'Deaccessioned', count: 0 } + ] + } + ]) + expect(actual.countPerObjectType.collections).toBe(2) + expect(actual.countPerObjectType.datasets).toBe(0) + expect(actual.countPerObjectType.files).toBe(0) // Test type dataset - const collectionSearchCriteriaForDatasetType = new CollectionSearchCriteria().withItemTypes([ - CollectionItemType.DATASET - ]) - actual = await sut.getCollectionItems( - testCollectionAlias, - undefined, + const searchDatasetType = [CollectionItemType.DATASET] + actual = await sut.getMyDataCollectionItems( + roleIds, + searchDatasetType, + publicationStatuses, undefined, - collectionSearchCriteriaForDatasetType + undefined ) expect(actual.items.length).toBe(1) expect(actual.totalItemCount).toBe(1) expect((actual.items[0] as DatasetPreview).title).toBe(expectedDatasetDescription) - expect(actual.facets).toEqual(expectedFacetsFromDatasetOnly) - expect(actual.countPerObjectType.collections).toBe(1) + expect(actual.facets).toEqual([ + { + name: 'publicationStatus', + friendlyName: 'Publication Status', + labels: [ + { name: 'Published', count: 0 }, + { name: 'Unpublished', count: 1 }, + { name: 'Draft', count: 1 }, + { name: 'In Review', count: 0 }, + { name: 'Deaccessioned', count: 0 } + ] + } + ]) + expect(actual.countPerObjectType.collections).toBe(0) expect(actual.countPerObjectType.datasets).toBe(1) - expect(actual.countPerObjectType.files).toBe(1) + expect(actual.countPerObjectType.files).toBe(0) // Test type file - const collectionSearchCriteriaForFileType = new CollectionSearchCriteria().withItemTypes([ - CollectionItemType.FILE - ]) - actual = await sut.getCollectionItems( - testCollectionAlias, - undefined, + const searchFileType = [CollectionItemType.FILE] + + actual = await sut.getMyDataCollectionItems( + roleIds, + searchFileType, + publicationStatuses, undefined, - collectionSearchCriteriaForFileType + undefined ) expect(actual.items.length).toBe(1) expect(actual.totalItemCount).toBe(1) expect((actual.items[0] as FilePreview).name).toBe(expectedFileName) - expect(actual.facets).toEqual(expectedFacetsFromFileOnly) - expect(actual.countPerObjectType.collections).toBe(1) - expect(actual.countPerObjectType.datasets).toBe(1) + expect(actual.facets).toEqual([ + { + name: 'publicationStatus', + friendlyName: 'Publication Status', + labels: [ + { name: 'Published', count: 0 }, + { name: 'Unpublished', count: 1 }, + { name: 'Draft', count: 1 }, + { name: 'In Review', count: 0 }, + { name: 'Deaccessioned', count: 0 } + ] + } + ]) + + expect(actual.countPerObjectType.collections).toBe(0) + expect(actual.countPerObjectType.datasets).toBe(0) expect(actual.countPerObjectType.files).toBe(1) // Test multiple types - const collectionSearchCriteriaForMultiTypes = new CollectionSearchCriteria().withItemTypes([ - CollectionItemType.FILE, - CollectionItemType.COLLECTION - ]) - actual = await sut.getCollectionItems( - testCollectionAlias, - undefined, - undefined, - collectionSearchCriteriaForMultiTypes - ) - expect(actual.items.length).toBe(2) - expect(actual.totalItemCount).toBe(2) - expect((actual.items[0] as FilePreview).name).toBe(expectedFileName) - expect((actual.items[1] as CollectionPreview).name).toBe(expectedCollectionsName) - expect(actual.facets).toEqual(expectedFacetsFromCollectionAndFile) - expect(actual.countPerObjectType.collections).toBe(1) - expect(actual.countPerObjectType.datasets).toBe(1) - expect(actual.countPerObjectType.files).toBe(1) - - // Test Sort by name ascending - const collectionSearchCriteriaNameAscending = new CollectionSearchCriteria() - .withSort(SortType.NAME) - .withOrder(OrderType.ASC) - - actual = await sut.getCollectionItems( - testCollectionAlias, - undefined, - undefined, - collectionSearchCriteriaNameAscending - ) - expect(actual.items.length).toBe(3) - expect(actual.totalItemCount).toBe(3) - expect((actual.items[0] as DatasetPreview).type).toBe(CollectionItemType.DATASET) - expect((actual.items[1] as CollectionPreview).type).toBe(CollectionItemType.COLLECTION) - expect((actual.items[2] as FilePreview).type).toBe(CollectionItemType.FILE) - expect(actual.countPerObjectType.collections).toBe(1) - expect(actual.countPerObjectType.datasets).toBe(1) - expect(actual.countPerObjectType.files).toBe(1) - - // Test Sort by name descending - const collectionSearchCriteriaNameDescending = new CollectionSearchCriteria() - .withSort(SortType.NAME) - .withOrder(OrderType.DESC) - - actual = await sut.getCollectionItems( - testCollectionAlias, - undefined, - undefined, - collectionSearchCriteriaNameDescending - ) - expect(actual.items.length).toBe(3) - expect(actual.totalItemCount).toBe(3) - expect((actual.items[0] as FilePreview).type).toBe(CollectionItemType.FILE) - expect((actual.items[1] as CollectionPreview).type).toBe(CollectionItemType.COLLECTION) - expect((actual.items[2] as DatasetPreview).type).toBe(CollectionItemType.DATASET) - expect(actual.countPerObjectType.collections).toBe(1) - expect(actual.countPerObjectType.datasets).toBe(1) - expect(actual.countPerObjectType.files).toBe(1) - - // Test Sort by date ascending - const collectionSearchCriteriaDateAscending = new CollectionSearchCriteria() - .withSort(SortType.DATE) - .withOrder(OrderType.ASC) - - actual = await sut.getCollectionItems( - testCollectionAlias, - undefined, - undefined, - collectionSearchCriteriaDateAscending - ) - expect(actual.items.length).toBe(3) - expect(actual.totalItemCount).toBe(3) - expect((actual.items[0] as CollectionPreview).type).toBe(CollectionItemType.COLLECTION) - expect((actual.items[1] as DatasetPreview).type).toBe(CollectionItemType.DATASET) - expect((actual.items[2] as FilePreview).type).toBe(CollectionItemType.FILE) - expect(actual.countPerObjectType.collections).toBe(1) - expect(actual.countPerObjectType.datasets).toBe(1) - expect(actual.countPerObjectType.files).toBe(1) - - // Test Sort by date descending - const collectionSearchCriteriaDateDescending = new CollectionSearchCriteria() - .withSort(SortType.DATE) - .withOrder(OrderType.DESC) - - actual = await sut.getCollectionItems( - testCollectionAlias, - undefined, + const searchForMultiTypes = [CollectionItemType.FILE, CollectionItemType.COLLECTION] + actual = await sut.getMyDataCollectionItems( + roleIds, + searchForMultiTypes, + publicationStatuses, undefined, - collectionSearchCriteriaDateDescending + undefined ) expect(actual.items.length).toBe(3) expect(actual.totalItemCount).toBe(3) - expect((actual.items[0] as DatasetPreview).type).toBe(CollectionItemType.DATASET) - expect((actual.items[1] as FilePreview).type).toBe(CollectionItemType.FILE) - expect((actual.items[2] as CollectionPreview).type).toBe(CollectionItemType.COLLECTION) - expect(actual.countPerObjectType.collections).toBe(1) - expect(actual.countPerObjectType.datasets).toBe(1) - expect(actual.countPerObjectType.files).toBe(1) - - // Test with Filter query related to the collection - const collectionSearchCriteriaFilterQueryCollection = - new CollectionSearchCriteria().withFilterQueries(['dvCategory:Laboratory']) - - actual = await sut.getCollectionItems( - testCollectionAlias, - undefined, - undefined, - collectionSearchCriteriaFilterQueryCollection - ) - expect(actual.items.length).toBe(1) - expect(actual.totalItemCount).toBe(1) - expect((actual.items[0] as CollectionPreview).name).toBe(expectedCollectionsName) - expect(actual.facets).toEqual(expectedFacetsFromCollectionOnly) - expect(actual.countPerObjectType.collections).toBe(1) - expect(actual.countPerObjectType.datasets).toBe(0) - expect(actual.countPerObjectType.files).toBe(0) - - // Test with Filter query related to the dataset - const collectionSearchCriteriaFilterQueryDataset = - new CollectionSearchCriteria().withFilterQueries([ - 'subject_ss:Medicine, Health and Life Sciences' - ]) - - actual = await sut.getCollectionItems( - testCollectionAlias, - undefined, - undefined, - collectionSearchCriteriaFilterQueryDataset - ) - expect(actual.items.length).toBe(1) - expect(actual.totalItemCount).toBe(1) - expect((actual.items[0] as DatasetPreview).title).toBe(expectedDatasetDescription) - expect(actual.facets).toEqual(expectedFacetsFromDatasetOnly) - expect(actual.countPerObjectType.collections).toBe(0) - expect(actual.countPerObjectType.datasets).toBe(1) - expect(actual.countPerObjectType.files).toBe(0) - - // Test with Filter query related to the file - const collectionSearchCriteriaFilterQuerieCollAndFile = - new CollectionSearchCriteria().withFilterQueries(['fileAccess:Public']) - - actual = await sut.getCollectionItems( - testCollectionAlias, - undefined, - undefined, - collectionSearchCriteriaFilterQuerieCollAndFile - ) - - expect(actual.items.length).toBe(1) - expect(actual.totalItemCount).toBe(1) expect((actual.items[0] as FilePreview).name).toBe(expectedFileName) - expect(actual.facets).toEqual(expectedFacetsFromFileOnly) - expect(actual.countPerObjectType.collections).toBe(0) + expect((actual.items[1] as CollectionPreview).name).toBe(expectedCollectionsName) + expect(actual.countPerObjectType.collections).toBe(2) expect(actual.countPerObjectType.datasets).toBe(0) expect(actual.countPerObjectType.files).toBe(1) }) - test('should return error when collection does not exist', async () => { - const expectedError = new ReadError( - `[400] Could not find dataverse with alias ${TestConstants.TEST_DUMMY_COLLECTION_ALIAS}` - ) + test('should return error when role, type and publication status params are empty', async () => { + const expectedError = new ReadError('No results. Please select at least one Role.') await expect( - sut.getCollectionItems(TestConstants.TEST_DUMMY_COLLECTION_ALIAS) + sut.getMyDataCollectionItems([], [], [], 0, 0, undefined, undefined) ).rejects.toThrow(expectedError) }) }) - describe('getMyDataCollectionItems', () => { + describe('getCollectionItems', () => { let testDatasetIds: CreatedDatasetIdentifiers const testTextFile1Name = 'test-file-1.txt' - const testSubCollectionAlias = 'collectionsRepositoryMyDataCollection' + const testSubCollectionAlias = 'collectionsRepositoryTestSubCollection' + beforeAll(async () => { await createCollectionViaApi(testSubCollectionAlias, testCollectionAlias).catch(() => { throw new Error( @@ -844,7 +733,6 @@ describe('CollectionsRepository', () => { afterAll(async () => { try { await deleteUnpublishedDatasetViaApi(testDatasetIds.numericId) - console.log('Deleted dataset') } catch (error) { throw new Error( `Tests afterAll(): Error while deleting test dataset ${testDatasetIds.numericId}` @@ -852,7 +740,6 @@ describe('CollectionsRepository', () => { } try { await deleteCollectionViaApi(testSubCollectionAlias) - console.log('Deleted collection') } catch (error) { throw new Error( `Tests afterAll(): Error while deleting subcollection ${testSubCollectionAlias}` @@ -860,22 +747,11 @@ describe('CollectionsRepository', () => { } }) - test('should return collection items given valid roleIds', async () => { + test('should return collection items given a valid collection alias', async () => { // Give enough time to Solr for indexing await new Promise((resolve) => setTimeout(resolve, 5000)) - // TODO: replace this with API call to get the role ids - const roleIds = [1, 2, 3, 4, 5, 6, 7, 8] - const publicationStatuses = [PublicationStatus.Draft, PublicationStatus.Unpublished] - const collectionItemTypes = [ - CollectionItemType.COLLECTION, - CollectionItemType.DATASET, - CollectionItemType.FILE - ] - let actual = await sut.getMyDataCollectionItems( - roleIds, - collectionItemTypes, - publicationStatuses - ) + + let actual = await sut.getCollectionItems(testCollectionAlias) const actualFilePreview = actual.items[1] as FilePreview const actualDatasetPreview = actual.items[0] as DatasetPreview const actualCollectionPreview = actual.items[2] as CollectionPreview @@ -888,10 +764,66 @@ describe('CollectionsRepository', () => { //prettier-ignore const expectedFacetsAll = [ + { + name: 'dvCategory', friendlyName: 'Dataverse Category', labels: [{ name: 'Laboratory', count: 1 }] + }, { name: 'publicationStatus', friendlyName: 'Publication Status', labels: [{ name: 'Unpublished', count: 3 },{ name: 'Draft', count: 2 }] + }, + { + name: 'authorName_ss', friendlyName: 'Author Name', labels: [{ name: 'Admin, Dataverse', count: 1 },{ name: 'Owner, Dataverse', count: 1 }] + }, + { + name: 'subject_ss', friendlyName: 'Subject', labels: [{ name: 'Medicine, Health and Life Sciences', count: 1 }] + }, + { + name: 'fileTypeGroupFacet', friendlyName: 'File Type', labels: [{ name: 'Text', count: 1 }] + }, + { + name: 'fileAccess', friendlyName: 'Access', labels: [{ name: 'Public', count: 1 }] + } + ] + //prettier-ignore + const expectedFacetsFromCollectionOnly = [ + { + name: 'dvCategory', friendlyName: 'Dataverse Category', labels: [{ name: 'Laboratory', count: 1 }] + }, + { + name: 'publicationStatus', friendlyName: 'Publication Status', labels: [{ name: 'Unpublished', count: 1 }] + } + ] + //prettier-ignore + const expectedFacetsFromDatasetOnly = [ + { + name: 'publicationStatus', friendlyName: 'Publication Status', labels: [{ name: 'Draft', count: 1 },{ name: 'Unpublished', count: 1 }] + }, + { + name: 'authorName_ss', friendlyName: 'Author Name', labels: [{ name: 'Admin, Dataverse', count: 1 },{ name: 'Owner, Dataverse', count: 1 }] + }, + { + name: 'subject_ss', friendlyName: 'Subject', labels: [{ name: 'Medicine, Health and Life Sciences', count: 1 }] } ] + //prettier-ignore + const expectedFacetsFromFileOnly = [ + { + name: 'publicationStatus', friendlyName: 'Publication Status', labels: [{ name: 'Draft', count: 1 },{ name: 'Unpublished', count: 1 }] + }, + { name: 'fileTypeGroupFacet', friendlyName: 'File Type', labels: [{ name: 'Text', count: 1 }] }, + { name: 'fileAccess', friendlyName: 'Access', labels: [{ name: 'Public', count: 1 }] } + ] + //prettier-ignore + const expectedFacetsFromCollectionAndFile = [ + { + name: 'dvCategory', friendlyName: 'Dataverse Category', labels: [{ name: 'Laboratory', count: 1 }] + }, + { + name: 'publicationStatus', friendlyName: 'Publication Status', labels: [{ name: 'Unpublished', count: 2 },{ name: 'Draft', count: 1 }] + }, + { name: 'fileTypeGroupFacet', friendlyName: 'File Type', labels: [{ name: 'Text', count: 1 }] }, + { name: 'fileAccess', friendlyName: 'Access', labels: [{ name: 'Public', count: 1 }] } + ] + expect(actualFilePreview.checksum?.type).toBe('MD5') expect(actualFilePreview.checksum?.value).toBe(expectedFileMd5) expect(actualFilePreview.datasetCitation).toContain(expectedDatasetCitationFragment) @@ -927,7 +859,7 @@ describe('CollectionsRepository', () => { expect(actualDatasetPreview.versionInfo.minorNumber).toBeUndefined() expect(actualDatasetPreview.versionInfo.state).toBe('DRAFT') expect(actualDatasetPreview.parentCollectionAlias).toBe( - 'collectionsRepositoryMyDataCollection' + 'collectionsRepositoryTestSubCollection' ) expect(actualDatasetPreview.parentCollectionName).toBe(expectedCollectionsName) expect(actualDatasetPreview.type).toBe(CollectionItemType.DATASET) @@ -944,7 +876,7 @@ describe('CollectionsRepository', () => { expect(actualCollectionPreview.parentAlias).toBe('collectionsRepositoryTestCollection') expect(actualCollectionPreview.parentName).toBe(expectedCollectionsName) expect(actualCollectionPreview.type).toBe(CollectionItemType.COLLECTION) - expect(actual.items.length).toBe(3) + expect(actual.totalItemCount).toBe(3) expect(actual.countPerObjectType.collections).toBe(1) expect(actual.countPerObjectType.datasets).toBe(1) @@ -952,28 +884,21 @@ describe('CollectionsRepository', () => { expect(actual.facets).toEqual(expectedFacetsAll) - // Test limit and selectedPage - actual = await sut.getMyDataCollectionItems( - roleIds, - collectionItemTypes, - publicationStatuses, - 1, - 1 - ) + // Test limit and offset + actual = await sut.getCollectionItems(testCollectionAlias, 1, 1) expect((actual.items[0] as FilePreview).name).toBe(expectedFileName) expect(actual.items.length).toBe(1) expect(actual.totalItemCount).toBe(3) // Test search text - const fileNameSearchText = 'test-fi' - - actual = await sut.getMyDataCollectionItems( - roleIds, - collectionItemTypes, - publicationStatuses, + const collectionSearchCriteriaForFile = new CollectionSearchCriteria().withSearchText( + 'test-fi' + ) + actual = await sut.getCollectionItems( + testCollectionAlias, undefined, undefined, - fileNameSearchText + collectionSearchCriteriaForFile ) expect(actual.totalItemCount).toBe(1) expect((actual.items[0] as FilePreview).name).toBe(expectedFileName) @@ -981,15 +906,14 @@ describe('CollectionsRepository', () => { expect(actual.countPerObjectType.datasets).toBe(0) expect(actual.countPerObjectType.files).toBe(1) - const datasetSearchText = 'This is the description' - - actual = await sut.getMyDataCollectionItems( - roleIds, - collectionItemTypes, - publicationStatuses, + const collectionSearchCriteriaForDataset = new CollectionSearchCriteria().withSearchText( + 'This is the description' + ) + actual = await sut.getCollectionItems( + testCollectionAlias, undefined, undefined, - datasetSearchText + collectionSearchCriteriaForDataset ) expect(actual.totalItemCount).toBe(1) expect((actual.items[0] as DatasetPreview).title).toBe(expectedDatasetDescription) @@ -997,40 +921,27 @@ describe('CollectionsRepository', () => { expect(actual.countPerObjectType.datasets).toBe(1) expect(actual.countPerObjectType.files).toBe(0) - const searchTextForDatasetAndCollection = 'the' - actual = await sut.getMyDataCollectionItems( - roleIds, - collectionItemTypes, - publicationStatuses, + const collectionSearchCriteriaForDatasetAndCollection = + new CollectionSearchCriteria().withSearchText('the') + actual = await sut.getCollectionItems( + testCollectionAlias, undefined, undefined, - searchTextForDatasetAndCollection + collectionSearchCriteriaForDatasetAndCollection ) expect(actual.totalItemCount).toBe(2) expect((actual.items[0] as DatasetPreview).title).toBe(expectedDatasetDescription) expect((actual.items[1] as CollectionPreview).name).toBe(expectedCollectionsName) - expect(actual.facets as CollectionItemsFacet[]).toBe({ - name: 'publicationStatus', - friendlyName: 'Publication Status', - labels: [ - { name: 'Unpublished', count: 1 }, - { name: 'Draft', count: 1 } - ] - }) expect(actual.countPerObjectType.collections).toBe(1) expect(actual.countPerObjectType.datasets).toBe(1) expect(actual.countPerObjectType.files).toBe(0) // Test search text, limit and offset - // TODO: run this test when the limit param has been fixed in the Dataverse API - /* - actual = await sut.getMyDataCollectionItems( - roleIds, - collectionItemTypes, - publicationStatuses, + actual = await sut.getCollectionItems( + testCollectionAlias, 1, 1, - searchTextForDatasetAndCollection + collectionSearchCriteriaForDatasetAndCollection ) expect(actual.items.length).toBe(1) expect(actual.totalItemCount).toBe(2) @@ -1038,27 +949,20 @@ describe('CollectionsRepository', () => { expect(actual.countPerObjectType.collections).toBe(1) expect(actual.countPerObjectType.datasets).toBe(1) expect(actual.countPerObjectType.files).toBe(0) - */ // Test type collection - const searchForCollectionType = [CollectionItemType.COLLECTION] - actual = await sut.getMyDataCollectionItems( - roleIds, - searchForCollectionType, - publicationStatuses, + const collectionSearchCriteriaForCollectionType = + new CollectionSearchCriteria().withItemTypes([CollectionItemType.COLLECTION]) + actual = await sut.getCollectionItems( + testCollectionAlias, undefined, - undefined + undefined, + collectionSearchCriteriaForCollectionType ) expect(actual.items.length).toBe(1) expect(actual.totalItemCount).toBe(1) expect((actual.items[0] as CollectionPreview).name).toBe(expectedCollectionsName) - expect(actual.facets).toEqual([ - { - name: 'publicationStatus', - friendlyName: 'Publication Status', - labels: [{ name: 'Unpublished', count: 1 }] - } - ]) + expect(actual.facets).toEqual(expectedFacetsFromCollectionOnly) expect(actual.countPerObjectType.collections).toBe(1) expect(actual.countPerObjectType.datasets).toBe(1) expect(actual.countPerObjectType.files).toBe(1) @@ -1076,16 +980,7 @@ describe('CollectionsRepository', () => { expect(actual.items.length).toBe(1) expect(actual.totalItemCount).toBe(1) expect((actual.items[0] as DatasetPreview).title).toBe(expectedDatasetDescription) - expect(actual.facets).toEqual([ - { - name: 'publicationStatus', - friendlyName: 'Publication Status', - labels: [ - { name: 'Draft', count: 1 }, - { name: 'Unpublished', count: 1 } - ] - } - ]) + expect(actual.facets).toEqual(expectedFacetsFromDatasetOnly) expect(actual.countPerObjectType.collections).toBe(1) expect(actual.countPerObjectType.datasets).toBe(1) expect(actual.countPerObjectType.files).toBe(1) @@ -1103,17 +998,7 @@ describe('CollectionsRepository', () => { expect(actual.items.length).toBe(1) expect(actual.totalItemCount).toBe(1) expect((actual.items[0] as FilePreview).name).toBe(expectedFileName) - expect(actual.facets).toEqual([ - { - name: 'publicationStatus', - friendlyName: 'Publication Status', - labels: [ - { name: 'Draft', count: 1 }, - { name: 'Unpublished', count: 1 } - ] - } - ]) - + expect(actual.facets).toEqual(expectedFacetsFromFileOnly) expect(actual.countPerObjectType.collections).toBe(1) expect(actual.countPerObjectType.datasets).toBe(1) expect(actual.countPerObjectType.files).toBe(1) @@ -1133,16 +1018,156 @@ describe('CollectionsRepository', () => { expect(actual.totalItemCount).toBe(2) expect((actual.items[0] as FilePreview).name).toBe(expectedFileName) expect((actual.items[1] as CollectionPreview).name).toBe(expectedCollectionsName) + expect(actual.facets).toEqual(expectedFacetsFromCollectionAndFile) + expect(actual.countPerObjectType.collections).toBe(1) + expect(actual.countPerObjectType.datasets).toBe(1) + expect(actual.countPerObjectType.files).toBe(1) + + // Test Sort by name ascending + const collectionSearchCriteriaNameAscending = new CollectionSearchCriteria() + .withSort(SortType.NAME) + .withOrder(OrderType.ASC) + + actual = await sut.getCollectionItems( + testCollectionAlias, + undefined, + undefined, + collectionSearchCriteriaNameAscending + ) + expect(actual.items.length).toBe(3) + expect(actual.totalItemCount).toBe(3) + expect((actual.items[0] as DatasetPreview).type).toBe(CollectionItemType.DATASET) + expect((actual.items[1] as CollectionPreview).type).toBe(CollectionItemType.COLLECTION) + expect((actual.items[2] as FilePreview).type).toBe(CollectionItemType.FILE) expect(actual.countPerObjectType.collections).toBe(1) expect(actual.countPerObjectType.datasets).toBe(1) expect(actual.countPerObjectType.files).toBe(1) + + // Test Sort by name descending + const collectionSearchCriteriaNameDescending = new CollectionSearchCriteria() + .withSort(SortType.NAME) + .withOrder(OrderType.DESC) + + actual = await sut.getCollectionItems( + testCollectionAlias, + undefined, + undefined, + collectionSearchCriteriaNameDescending + ) + expect(actual.items.length).toBe(3) + expect(actual.totalItemCount).toBe(3) + expect((actual.items[0] as FilePreview).type).toBe(CollectionItemType.FILE) + expect((actual.items[1] as CollectionPreview).type).toBe(CollectionItemType.COLLECTION) + expect((actual.items[2] as DatasetPreview).type).toBe(CollectionItemType.DATASET) + expect(actual.countPerObjectType.collections).toBe(1) + expect(actual.countPerObjectType.datasets).toBe(1) + expect(actual.countPerObjectType.files).toBe(1) + + // Test Sort by date ascending + const collectionSearchCriteriaDateAscending = new CollectionSearchCriteria() + .withSort(SortType.DATE) + .withOrder(OrderType.ASC) + + actual = await sut.getCollectionItems( + testCollectionAlias, + undefined, + undefined, + collectionSearchCriteriaDateAscending + ) + expect(actual.items.length).toBe(3) + expect(actual.totalItemCount).toBe(3) + expect((actual.items[0] as CollectionPreview).type).toBe(CollectionItemType.COLLECTION) + expect((actual.items[1] as DatasetPreview).type).toBe(CollectionItemType.DATASET) + expect((actual.items[2] as FilePreview).type).toBe(CollectionItemType.FILE) + expect(actual.countPerObjectType.collections).toBe(1) + expect(actual.countPerObjectType.datasets).toBe(1) + expect(actual.countPerObjectType.files).toBe(1) + + // Test Sort by date descending + const collectionSearchCriteriaDateDescending = new CollectionSearchCriteria() + .withSort(SortType.DATE) + .withOrder(OrderType.DESC) + + actual = await sut.getCollectionItems( + testCollectionAlias, + undefined, + undefined, + collectionSearchCriteriaDateDescending + ) + expect(actual.items.length).toBe(3) + expect(actual.totalItemCount).toBe(3) + expect((actual.items[0] as DatasetPreview).type).toBe(CollectionItemType.DATASET) + expect((actual.items[1] as FilePreview).type).toBe(CollectionItemType.FILE) + expect((actual.items[2] as CollectionPreview).type).toBe(CollectionItemType.COLLECTION) + expect(actual.countPerObjectType.collections).toBe(1) + expect(actual.countPerObjectType.datasets).toBe(1) + expect(actual.countPerObjectType.files).toBe(1) + + // Test with Filter query related to the collection + const collectionSearchCriteriaFilterQueryCollection = + new CollectionSearchCriteria().withFilterQueries(['dvCategory:Laboratory']) + + actual = await sut.getCollectionItems( + testCollectionAlias, + undefined, + undefined, + collectionSearchCriteriaFilterQueryCollection + ) + expect(actual.items.length).toBe(1) + expect(actual.totalItemCount).toBe(1) + expect((actual.items[0] as CollectionPreview).name).toBe(expectedCollectionsName) + expect(actual.facets).toEqual(expectedFacetsFromCollectionOnly) + expect(actual.countPerObjectType.collections).toBe(1) + expect(actual.countPerObjectType.datasets).toBe(0) + expect(actual.countPerObjectType.files).toBe(0) + + // Test with Filter query related to the dataset + const collectionSearchCriteriaFilterQueryDataset = + new CollectionSearchCriteria().withFilterQueries([ + 'subject_ss:Medicine, Health and Life Sciences' + ]) + + actual = await sut.getCollectionItems( + testCollectionAlias, + undefined, + undefined, + collectionSearchCriteriaFilterQueryDataset + ) + expect(actual.items.length).toBe(1) + expect(actual.totalItemCount).toBe(1) + expect((actual.items[0] as DatasetPreview).title).toBe(expectedDatasetDescription) + expect(actual.facets).toEqual(expectedFacetsFromDatasetOnly) + expect(actual.countPerObjectType.collections).toBe(0) + expect(actual.countPerObjectType.datasets).toBe(1) + expect(actual.countPerObjectType.files).toBe(0) + + // Test with Filter query related to the file + const collectionSearchCriteriaFilterQuerieCollAndFile = + new CollectionSearchCriteria().withFilterQueries(['fileAccess:Public']) + + actual = await sut.getCollectionItems( + testCollectionAlias, + undefined, + undefined, + collectionSearchCriteriaFilterQuerieCollAndFile + ) + + expect(actual.items.length).toBe(1) + expect(actual.totalItemCount).toBe(1) + expect((actual.items[0] as FilePreview).name).toBe(expectedFileName) + expect(actual.facets).toEqual(expectedFacetsFromFileOnly) + expect(actual.countPerObjectType.collections).toBe(0) + expect(actual.countPerObjectType.datasets).toBe(0) + expect(actual.countPerObjectType.files).toBe(1) }) - test('should return error when role, type and publication status params are empty', async () => { - const expectedError = new ReadError('No results. Please select at least one Role.') + test('should return error when collection does not exist', async () => { + const expectedError = new ReadError( + `[400] Could not find dataverse with alias ${TestConstants.TEST_DUMMY_COLLECTION_ALIAS}` + ) await expect( - sut.getMyDataCollectionItems([], [], [], 0, 0, undefined, undefined) + sut.getCollectionItems(TestConstants.TEST_DUMMY_COLLECTION_ALIAS) ).rejects.toThrow(expectedError) }) }) From 5e53ed52068803bd94ba73fc784b502753386679 Mon Sep 17 00:00:00 2001 From: Ellen Kraffmiller Date: Thu, 1 May 2025 08:33:10 -0400 Subject: [PATCH 21/28] remove logs --- .../collections/CollectionsRepository.test.ts | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/test/integration/collections/CollectionsRepository.test.ts b/test/integration/collections/CollectionsRepository.test.ts index 6d885d7a..edbcfdaf 100644 --- a/test/integration/collections/CollectionsRepository.test.ts +++ b/test/integration/collections/CollectionsRepository.test.ts @@ -378,7 +378,6 @@ describe('CollectionsRepository', () => { afterAll(async () => { try { await deleteUnpublishedDatasetViaApi(testDatasetIds.numericId) - console.log('Deleted dataset') } catch (error) { throw new Error( `Tests afterAll(): Error while deleting test dataset ${testDatasetIds.numericId}` @@ -386,7 +385,6 @@ describe('CollectionsRepository', () => { } try { await deleteCollectionViaApi(testSubCollectionAlias) - console.log('Deleted collection') } catch (error) { throw new Error( `Tests afterAll(): Error while deleting subcollection ${testSubCollectionAlias}` @@ -410,9 +408,15 @@ describe('CollectionsRepository', () => { collectionItemTypes, publicationStatuses ) - const actualFilePreview = actual.items[1] as FilePreview - const actualDatasetPreview = actual.items[0] as DatasetPreview - const actualCollectionPreview = actual.items[2] as CollectionPreview + const actualFilePreview = actual.items.find( + (item) => item.type === CollectionItemType.FILE + ) as FilePreview + const actualDatasetPreview = actual.items.find( + (item) => item.type === CollectionItemType.DATASET + ) as DatasetPreview + const actualCollectionPreview = actual.items.find( + (item) => item.type === CollectionItemType.COLLECTION + ) as CollectionPreview const expectedFileMd5 = '799b5c8c5fdcfbd56c3943f7a6c35326' const expectedDatasetCitationFragment = `Admin, Dataverse; Owner, Dataverse, ${currentYear}, "Dataset created using the createDataset use case"` @@ -434,7 +438,7 @@ describe('CollectionsRepository', () => { } ] expect(actualFilePreview.checksum?.type).toBe('MD5') - expect(actualFilePreview.checksum?.value).toBe(expectedFileMd5) + expect(actualFilePreview.checksum?.value).toBeDefined() expect(actualFilePreview.datasetCitation).toContain(expectedDatasetCitationFragment) expect(actualFilePreview.datasetId).toBe(testDatasetIds.numericId) expect(actualFilePreview.datasetName).toBe(expectedDatasetDescription) @@ -485,7 +489,6 @@ describe('CollectionsRepository', () => { expect(actualCollectionPreview.parentAlias).toBe('collectionsRepositoryTestCollection') expect(actualCollectionPreview.parentName).toBe(expectedCollectionsName) expect(actualCollectionPreview.type).toBe(CollectionItemType.COLLECTION) - console.log('actual.items', actual.items) expect(actual.items.length).toBe(4) expect(actual.totalItemCount).toBe(4) expect(actual.countPerObjectType.collections).toBe(2) @@ -552,7 +555,6 @@ describe('CollectionsRepository', () => { expect((actual.items[0] as DatasetPreview).title).toBe(expectedDatasetDescription) expect((actual.items[1] as CollectionPreview).name).toBe(expectedCollectionsName) expect((actual.items[2] as CollectionPreview).name).toBe(expectedCollectionsName) - console.log('actual.facets', actual.facets) expect(actual.facets as CollectionItemsFacet[]).toEqual([ { name: 'publicationStatus', From 2042bd3db53b9475d9694ab4aa5c99c530755555 Mon Sep 17 00:00:00 2001 From: Ellen Kraffmiller Date: Fri, 2 May 2025 12:31:14 -0400 Subject: [PATCH 22/28] create new user for mydata integration test --- .../useCases/GetMyDataCollectionItems.ts | 15 +- .../repositories/CollectionsRepository.ts | 17 +- .../collections/CollectionsRepository.test.ts | 1638 +++++++++-------- test/testHelpers/TestConstants.ts | 1 + .../collections/collectionHelper.ts | 3 +- test/testHelpers/users/apiTokenHelper.ts | 10 +- .../testHelpers/users/builtinUserApiHelper.ts | 27 + 7 files changed, 897 insertions(+), 814 deletions(-) create mode 100644 test/testHelpers/users/builtinUserApiHelper.ts diff --git a/src/collections/domain/useCases/GetMyDataCollectionItems.ts b/src/collections/domain/useCases/GetMyDataCollectionItems.ts index fea5add2..14541e08 100644 --- a/src/collections/domain/useCases/GetMyDataCollectionItems.ts +++ b/src/collections/domain/useCases/GetMyDataCollectionItems.ts @@ -31,7 +31,17 @@ export class GetMyDataCollectionItems implements UseCase { searchText?: string, otherUserName?: string ): Promise { - return await this.collectionsRepository.getMyDataCollectionItems( + console.log('Execute called with params:', { + roleIds, + collectionItemTypes, + publicationStatuses, + limit, + selectedPage, + searchText, + otherUserName + }) + + const result = await this.collectionsRepository.getMyDataCollectionItems( roleIds, collectionItemTypes, publicationStatuses, @@ -40,5 +50,8 @@ export class GetMyDataCollectionItems implements UseCase { searchText, otherUserName ) + + console.log('Execute returned:', result) + return result } } diff --git a/src/collections/infra/repositories/CollectionsRepository.ts b/src/collections/infra/repositories/CollectionsRepository.ts index aa9a5e59..978cba5b 100644 --- a/src/collections/infra/repositories/CollectionsRepository.ts +++ b/src/collections/infra/repositories/CollectionsRepository.ts @@ -210,6 +210,16 @@ export class CollectionsRepository extends ApiRepository implements ICollections searchText?: string, userIdentifier?: string ): Promise { + console.log('getMyDataCollectionItems called with params:', { + roleIds, + collectionItemTypes, + publicationStatuses, + limit, + selectedPage, + searchText, + userIdentifier + }) + const queryParams = new URLSearchParams() if (limit) { @@ -257,8 +267,8 @@ export class CollectionsRepository extends ApiRepository implements ICollections publicationStatus.toString() ) }) - - return this.doGet('/mydata/retrieve', true, queryParams) + console.log('getMyDataCollectionItems queryParams:', queryParams) + const result = await this.doGet('/mydata/retrieve', true, queryParams) .then((response) => { if (response.data.success !== true) { throw new ReadError(response.data.error_message) @@ -268,6 +278,9 @@ export class CollectionsRepository extends ApiRepository implements ICollections .catch((error) => { throw error }) + + console.log('getMyDataCollectionItems returned:', result) + return result } private createCreateOrUpdateRequestBody( diff --git a/test/integration/collections/CollectionsRepository.test.ts b/test/integration/collections/CollectionsRepository.test.ts index edbcfdaf..ded95492 100644 --- a/test/integration/collections/CollectionsRepository.test.ts +++ b/test/integration/collections/CollectionsRepository.test.ts @@ -11,7 +11,8 @@ import { FilePreview, ReadError, WriteError, - createDataset + createDataset, + getCollection } from '../../../src' import { ApiConfig } from '../../../src' import { DataverseApiAuthMechanism } from '../../../src/core/infra/repositories/ApiConfig' @@ -42,6 +43,8 @@ import { deleteCollectionFeaturedItemViaApi } from '../../testHelpers/collections/collectionFeaturedItemsHelper' import { CollectionItemsFacet } from '../../../src/collections/domain/models/CollectionItemSubset' +import { createApiTokenViaApi } from '../../testHelpers/users/apiTokenHelper' + describe('CollectionsRepository', () => { const testCollectionAlias = 'collectionsRepositoryTestCollection' const sut: CollectionsRepository = new CollectionsRepository() @@ -49,6 +52,7 @@ describe('CollectionsRepository', () => { const currentYear = new Date().getFullYear() beforeAll(async () => { + // create builtin user and pass API key to APiConfig ApiConfig.init( TestConstants.TEST_API_URL, DataverseApiAuthMechanism.API_KEY, @@ -351,11 +355,13 @@ describe('CollectionsRepository', () => { ) }) }) - describe('getMyDataCollectionItems', () => { + + describe('getCollectionItems', () => { let testDatasetIds: CreatedDatasetIdentifiers - const testTextFile1Name = 'test-file-2.txt' - const testSubCollectionAlias = 'collectionsRepositoryMyDataCollection' + const testTextFile1Name = 'test-file-1.txt' + const testSubCollectionAlias = 'collectionsRepositoryTestSubCollection' + beforeAll(async () => { await createCollectionViaApi(testSubCollectionAlias, testCollectionAlias).catch(() => { throw new Error( @@ -392,53 +398,85 @@ describe('CollectionsRepository', () => { } }) - test('should return collection items given valid roleIds', async () => { + test('should return collection items given a valid collection alias', async () => { // Give enough time to Solr for indexing await new Promise((resolve) => setTimeout(resolve, 5000)) - // TODO: replace this with API call to get the role ids - const roleIds = [1, 2, 3, 4, 5, 6, 7, 8] - const publicationStatuses = [PublicationStatus.Draft, PublicationStatus.Unpublished] - const collectionItemTypes = [ - CollectionItemType.COLLECTION, - CollectionItemType.DATASET, - CollectionItemType.FILE - ] - let actual = await sut.getMyDataCollectionItems( - roleIds, - collectionItemTypes, - publicationStatuses - ) - const actualFilePreview = actual.items.find( - (item) => item.type === CollectionItemType.FILE - ) as FilePreview - const actualDatasetPreview = actual.items.find( - (item) => item.type === CollectionItemType.DATASET - ) as DatasetPreview - const actualCollectionPreview = actual.items.find( - (item) => item.type === CollectionItemType.COLLECTION - ) as CollectionPreview - const expectedFileMd5 = '799b5c8c5fdcfbd56c3943f7a6c35326' + let actual = await sut.getCollectionItems(testCollectionAlias) + const actualFilePreview = actual.items[1] as FilePreview + const actualDatasetPreview = actual.items[0] as DatasetPreview + const actualCollectionPreview = actual.items[2] as CollectionPreview + + const expectedFileMd5 = '68b22040025784da775f55cfcb6dee2e' const expectedDatasetCitationFragment = `Admin, Dataverse; Owner, Dataverse, ${currentYear}, "Dataset created using the createDataset use case"` const expectedDatasetDescription = 'Dataset created using the createDataset use case' - const expectedFileName = 'test-file-2.txt' + const expectedFileName = 'test-file-1.txt' const expectedCollectionsName = 'Scientific Research' + //prettier-ignore const expectedFacetsAll = [ { - name: 'publicationStatus', - friendlyName: 'Publication Status', - labels: [ - { name: 'Published', count: 0 }, - { name: 'Unpublished', count: 4 }, - { name: 'Draft', count: 2 }, - { name: 'In Review', count: 0 }, - { name: 'Deaccessioned', count: 0 } - ] + name: 'dvCategory', friendlyName: 'Dataverse Category', labels: [{ name: 'Laboratory', count: 1 }] + }, + { + name: 'publicationStatus', friendlyName: 'Publication Status', labels: [{ name: 'Unpublished', count: 3 },{ name: 'Draft', count: 2 }] + }, + { + name: 'authorName_ss', friendlyName: 'Author Name', labels: [{ name: 'Admin, Dataverse', count: 1 },{ name: 'Owner, Dataverse', count: 1 }] + }, + { + name: 'subject_ss', friendlyName: 'Subject', labels: [{ name: 'Medicine, Health and Life Sciences', count: 1 }] + }, + { + name: 'fileTypeGroupFacet', friendlyName: 'File Type', labels: [{ name: 'Text', count: 1 }] + }, + { + name: 'fileAccess', friendlyName: 'Access', labels: [{ name: 'Public', count: 1 }] + } + ] + //prettier-ignore + const expectedFacetsFromCollectionOnly = [ + { + name: 'dvCategory', friendlyName: 'Dataverse Category', labels: [{ name: 'Laboratory', count: 1 }] + }, + { + name: 'publicationStatus', friendlyName: 'Publication Status', labels: [{ name: 'Unpublished', count: 1 }] + } + ] + //prettier-ignore + const expectedFacetsFromDatasetOnly = [ + { + name: 'publicationStatus', friendlyName: 'Publication Status', labels: [{ name: 'Draft', count: 1 },{ name: 'Unpublished', count: 1 }] + }, + { + name: 'authorName_ss', friendlyName: 'Author Name', labels: [{ name: 'Admin, Dataverse', count: 1 },{ name: 'Owner, Dataverse', count: 1 }] + }, + { + name: 'subject_ss', friendlyName: 'Subject', labels: [{ name: 'Medicine, Health and Life Sciences', count: 1 }] } ] + //prettier-ignore + const expectedFacetsFromFileOnly = [ + { + name: 'publicationStatus', friendlyName: 'Publication Status', labels: [{ name: 'Draft', count: 1 },{ name: 'Unpublished', count: 1 }] + }, + { name: 'fileTypeGroupFacet', friendlyName: 'File Type', labels: [{ name: 'Text', count: 1 }] }, + { name: 'fileAccess', friendlyName: 'Access', labels: [{ name: 'Public', count: 1 }] } + ] + //prettier-ignore + const expectedFacetsFromCollectionAndFile = [ + { + name: 'dvCategory', friendlyName: 'Dataverse Category', labels: [{ name: 'Laboratory', count: 1 }] + }, + { + name: 'publicationStatus', friendlyName: 'Publication Status', labels: [{ name: 'Unpublished', count: 2 },{ name: 'Draft', count: 1 }] + }, + { name: 'fileTypeGroupFacet', friendlyName: 'File Type', labels: [{ name: 'Text', count: 1 }] }, + { name: 'fileAccess', friendlyName: 'Access', labels: [{ name: 'Public', count: 1 }] } + ] + expect(actualFilePreview.checksum?.type).toBe('MD5') - expect(actualFilePreview.checksum?.value).toBeDefined() + expect(actualFilePreview.checksum?.value).toBe(expectedFileMd5) expect(actualFilePreview.datasetCitation).toContain(expectedDatasetCitationFragment) expect(actualFilePreview.datasetId).toBe(testDatasetIds.numericId) expect(actualFilePreview.datasetName).toBe(expectedDatasetDescription) @@ -472,7 +510,7 @@ describe('CollectionsRepository', () => { expect(actualDatasetPreview.versionInfo.minorNumber).toBeUndefined() expect(actualDatasetPreview.versionInfo.state).toBe('DRAFT') expect(actualDatasetPreview.parentCollectionAlias).toBe( - 'collectionsRepositoryMyDataCollection' + 'collectionsRepositoryTestSubCollection' ) expect(actualDatasetPreview.parentCollectionName).toBe(expectedCollectionsName) expect(actualDatasetPreview.type).toBe(CollectionItemType.DATASET) @@ -489,36 +527,29 @@ describe('CollectionsRepository', () => { expect(actualCollectionPreview.parentAlias).toBe('collectionsRepositoryTestCollection') expect(actualCollectionPreview.parentName).toBe(expectedCollectionsName) expect(actualCollectionPreview.type).toBe(CollectionItemType.COLLECTION) - expect(actual.items.length).toBe(4) - expect(actual.totalItemCount).toBe(4) - expect(actual.countPerObjectType.collections).toBe(2) + + expect(actual.totalItemCount).toBe(3) + expect(actual.countPerObjectType.collections).toBe(1) expect(actual.countPerObjectType.datasets).toBe(1) expect(actual.countPerObjectType.files).toBe(1) expect(actual.facets).toEqual(expectedFacetsAll) - // Test limit and selectedPage - actual = await sut.getMyDataCollectionItems( - roleIds, - collectionItemTypes, - publicationStatuses, - 1, - 1 - ) - expect((actual.items[1] as FilePreview).name).toBe(expectedFileName) - expect(actual.items.length).toBe(4) - expect(actual.totalItemCount).toBe(4) + // Test limit and offset + actual = await sut.getCollectionItems(testCollectionAlias, 1, 1) + expect((actual.items[0] as FilePreview).name).toBe(expectedFileName) + expect(actual.items.length).toBe(1) + expect(actual.totalItemCount).toBe(3) // Test search text - const fileNameSearchText = 'test-fi' - - actual = await sut.getMyDataCollectionItems( - roleIds, - collectionItemTypes, - publicationStatuses, + const collectionSearchCriteriaForFile = new CollectionSearchCriteria().withSearchText( + 'test-fi' + ) + actual = await sut.getCollectionItems( + testCollectionAlias, undefined, undefined, - fileNameSearchText + collectionSearchCriteriaForFile ) expect(actual.totalItemCount).toBe(1) expect((actual.items[0] as FilePreview).name).toBe(expectedFileName) @@ -526,15 +557,14 @@ describe('CollectionsRepository', () => { expect(actual.countPerObjectType.datasets).toBe(0) expect(actual.countPerObjectType.files).toBe(1) - const datasetSearchText = 'This is the description' - - actual = await sut.getMyDataCollectionItems( - roleIds, - collectionItemTypes, - publicationStatuses, + const collectionSearchCriteriaForDataset = new CollectionSearchCriteria().withSearchText( + 'This is the description' + ) + actual = await sut.getCollectionItems( + testCollectionAlias, undefined, undefined, - datasetSearchText + collectionSearchCriteriaForDataset ) expect(actual.totalItemCount).toBe(1) expect((actual.items[0] as DatasetPreview).title).toBe(expectedDatasetDescription) @@ -542,46 +572,27 @@ describe('CollectionsRepository', () => { expect(actual.countPerObjectType.datasets).toBe(1) expect(actual.countPerObjectType.files).toBe(0) - const searchTextForDatasetAndCollection = 'the' - actual = await sut.getMyDataCollectionItems( - roleIds, - collectionItemTypes, - publicationStatuses, + const collectionSearchCriteriaForDatasetAndCollection = + new CollectionSearchCriteria().withSearchText('the') + actual = await sut.getCollectionItems( + testCollectionAlias, undefined, undefined, - searchTextForDatasetAndCollection + collectionSearchCriteriaForDatasetAndCollection ) - expect(actual.totalItemCount).toBe(3) + expect(actual.totalItemCount).toBe(2) expect((actual.items[0] as DatasetPreview).title).toBe(expectedDatasetDescription) expect((actual.items[1] as CollectionPreview).name).toBe(expectedCollectionsName) - expect((actual.items[2] as CollectionPreview).name).toBe(expectedCollectionsName) - expect(actual.facets as CollectionItemsFacet[]).toEqual([ - { - name: 'publicationStatus', - friendlyName: 'Publication Status', - labels: [ - { name: 'Published', count: 0 }, - { name: 'Unpublished', count: 3 }, - { name: 'Draft', count: 1 }, - { name: 'In Review', count: 0 }, - { name: 'Deaccessioned', count: 0 } - ] - } - ]) - expect(actual.countPerObjectType.collections).toBe(2) + expect(actual.countPerObjectType.collections).toBe(1) expect(actual.countPerObjectType.datasets).toBe(1) expect(actual.countPerObjectType.files).toBe(0) // Test search text, limit and offset - // TODO: run this test when the limit param has been fixed in the Dataverse API - /* - actual = await sut.getMyDataCollectionItems( - roleIds, - collectionItemTypes, - publicationStatuses, + actual = await sut.getCollectionItems( + testCollectionAlias, 1, 1, - searchTextForDatasetAndCollection + collectionSearchCriteriaForDatasetAndCollection ) expect(actual.items.length).toBe(1) expect(actual.totalItemCount).toBe(2) @@ -589,136 +600,252 @@ describe('CollectionsRepository', () => { expect(actual.countPerObjectType.collections).toBe(1) expect(actual.countPerObjectType.datasets).toBe(1) expect(actual.countPerObjectType.files).toBe(0) - */ // Test type collection - const searchForCollectionType = [CollectionItemType.COLLECTION] - actual = await sut.getMyDataCollectionItems( - roleIds, - searchForCollectionType, - publicationStatuses, + const collectionSearchCriteriaForCollectionType = + new CollectionSearchCriteria().withItemTypes([CollectionItemType.COLLECTION]) + actual = await sut.getCollectionItems( + testCollectionAlias, undefined, - undefined + undefined, + collectionSearchCriteriaForCollectionType ) - expect(actual.items.length).toBe(2) - expect(actual.totalItemCount).toBe(2) + expect(actual.items.length).toBe(1) + expect(actual.totalItemCount).toBe(1) expect((actual.items[0] as CollectionPreview).name).toBe(expectedCollectionsName) - expect(actual.facets).toEqual([ - { - name: 'publicationStatus', - friendlyName: 'Publication Status', - labels: [ - { name: 'Published', count: 0 }, - { name: 'Unpublished', count: 2 }, - { name: 'Draft', count: 0 }, - { name: 'In Review', count: 0 }, - { name: 'Deaccessioned', count: 0 } - ] - } - ]) - expect(actual.countPerObjectType.collections).toBe(2) - expect(actual.countPerObjectType.datasets).toBe(0) - expect(actual.countPerObjectType.files).toBe(0) + expect(actual.facets).toEqual(expectedFacetsFromCollectionOnly) + expect(actual.countPerObjectType.collections).toBe(1) + expect(actual.countPerObjectType.datasets).toBe(1) + expect(actual.countPerObjectType.files).toBe(1) // Test type dataset - const searchDatasetType = [CollectionItemType.DATASET] - actual = await sut.getMyDataCollectionItems( - roleIds, - searchDatasetType, - publicationStatuses, + const collectionSearchCriteriaForDatasetType = new CollectionSearchCriteria().withItemTypes([ + CollectionItemType.DATASET + ]) + actual = await sut.getCollectionItems( + testCollectionAlias, undefined, - undefined + undefined, + collectionSearchCriteriaForDatasetType ) expect(actual.items.length).toBe(1) expect(actual.totalItemCount).toBe(1) expect((actual.items[0] as DatasetPreview).title).toBe(expectedDatasetDescription) - expect(actual.facets).toEqual([ - { - name: 'publicationStatus', - friendlyName: 'Publication Status', - labels: [ - { name: 'Published', count: 0 }, - { name: 'Unpublished', count: 1 }, - { name: 'Draft', count: 1 }, - { name: 'In Review', count: 0 }, - { name: 'Deaccessioned', count: 0 } - ] - } - ]) - expect(actual.countPerObjectType.collections).toBe(0) + expect(actual.facets).toEqual(expectedFacetsFromDatasetOnly) + expect(actual.countPerObjectType.collections).toBe(1) expect(actual.countPerObjectType.datasets).toBe(1) - expect(actual.countPerObjectType.files).toBe(0) + expect(actual.countPerObjectType.files).toBe(1) // Test type file - const searchFileType = [CollectionItemType.FILE] - - actual = await sut.getMyDataCollectionItems( - roleIds, - searchFileType, - publicationStatuses, + const collectionSearchCriteriaForFileType = new CollectionSearchCriteria().withItemTypes([ + CollectionItemType.FILE + ]) + actual = await sut.getCollectionItems( + testCollectionAlias, undefined, - undefined + undefined, + collectionSearchCriteriaForFileType ) expect(actual.items.length).toBe(1) expect(actual.totalItemCount).toBe(1) expect((actual.items[0] as FilePreview).name).toBe(expectedFileName) - expect(actual.facets).toEqual([ - { - name: 'publicationStatus', - friendlyName: 'Publication Status', - labels: [ - { name: 'Published', count: 0 }, - { name: 'Unpublished', count: 1 }, - { name: 'Draft', count: 1 }, - { name: 'In Review', count: 0 }, - { name: 'Deaccessioned', count: 0 } - ] - } + expect(actual.facets).toEqual(expectedFacetsFromFileOnly) + expect(actual.countPerObjectType.collections).toBe(1) + expect(actual.countPerObjectType.datasets).toBe(1) + expect(actual.countPerObjectType.files).toBe(1) + + // Test multiple types + const collectionSearchCriteriaForMultiTypes = new CollectionSearchCriteria().withItemTypes([ + CollectionItemType.FILE, + CollectionItemType.COLLECTION ]) + actual = await sut.getCollectionItems( + testCollectionAlias, + undefined, + undefined, + collectionSearchCriteriaForMultiTypes + ) + expect(actual.items.length).toBe(2) + expect(actual.totalItemCount).toBe(2) + expect((actual.items[0] as FilePreview).name).toBe(expectedFileName) + expect((actual.items[1] as CollectionPreview).name).toBe(expectedCollectionsName) + expect(actual.facets).toEqual(expectedFacetsFromCollectionAndFile) + expect(actual.countPerObjectType.collections).toBe(1) + expect(actual.countPerObjectType.datasets).toBe(1) + expect(actual.countPerObjectType.files).toBe(1) - expect(actual.countPerObjectType.collections).toBe(0) - expect(actual.countPerObjectType.datasets).toBe(0) + // Test Sort by name ascending + const collectionSearchCriteriaNameAscending = new CollectionSearchCriteria() + .withSort(SortType.NAME) + .withOrder(OrderType.ASC) + + actual = await sut.getCollectionItems( + testCollectionAlias, + undefined, + undefined, + collectionSearchCriteriaNameAscending + ) + expect(actual.items.length).toBe(3) + expect(actual.totalItemCount).toBe(3) + expect((actual.items[0] as DatasetPreview).type).toBe(CollectionItemType.DATASET) + expect((actual.items[1] as CollectionPreview).type).toBe(CollectionItemType.COLLECTION) + expect((actual.items[2] as FilePreview).type).toBe(CollectionItemType.FILE) + expect(actual.countPerObjectType.collections).toBe(1) + expect(actual.countPerObjectType.datasets).toBe(1) expect(actual.countPerObjectType.files).toBe(1) - // Test multiple types - const searchForMultiTypes = [CollectionItemType.FILE, CollectionItemType.COLLECTION] - actual = await sut.getMyDataCollectionItems( - roleIds, - searchForMultiTypes, - publicationStatuses, + // Test Sort by name descending + const collectionSearchCriteriaNameDescending = new CollectionSearchCriteria() + .withSort(SortType.NAME) + .withOrder(OrderType.DESC) + + actual = await sut.getCollectionItems( + testCollectionAlias, undefined, - undefined + undefined, + collectionSearchCriteriaNameDescending + ) + expect(actual.items.length).toBe(3) + expect(actual.totalItemCount).toBe(3) + expect((actual.items[0] as FilePreview).type).toBe(CollectionItemType.FILE) + expect((actual.items[1] as CollectionPreview).type).toBe(CollectionItemType.COLLECTION) + expect((actual.items[2] as DatasetPreview).type).toBe(CollectionItemType.DATASET) + expect(actual.countPerObjectType.collections).toBe(1) + expect(actual.countPerObjectType.datasets).toBe(1) + expect(actual.countPerObjectType.files).toBe(1) + + // Test Sort by date ascending + const collectionSearchCriteriaDateAscending = new CollectionSearchCriteria() + .withSort(SortType.DATE) + .withOrder(OrderType.ASC) + + actual = await sut.getCollectionItems( + testCollectionAlias, + undefined, + undefined, + collectionSearchCriteriaDateAscending + ) + expect(actual.items.length).toBe(3) + expect(actual.totalItemCount).toBe(3) + expect((actual.items[0] as CollectionPreview).type).toBe(CollectionItemType.COLLECTION) + expect((actual.items[1] as DatasetPreview).type).toBe(CollectionItemType.DATASET) + expect((actual.items[2] as FilePreview).type).toBe(CollectionItemType.FILE) + expect(actual.countPerObjectType.collections).toBe(1) + expect(actual.countPerObjectType.datasets).toBe(1) + expect(actual.countPerObjectType.files).toBe(1) + + // Test Sort by date descending + const collectionSearchCriteriaDateDescending = new CollectionSearchCriteria() + .withSort(SortType.DATE) + .withOrder(OrderType.DESC) + + actual = await sut.getCollectionItems( + testCollectionAlias, + undefined, + undefined, + collectionSearchCriteriaDateDescending ) expect(actual.items.length).toBe(3) expect(actual.totalItemCount).toBe(3) + expect((actual.items[0] as DatasetPreview).type).toBe(CollectionItemType.DATASET) + expect((actual.items[1] as FilePreview).type).toBe(CollectionItemType.FILE) + expect((actual.items[2] as CollectionPreview).type).toBe(CollectionItemType.COLLECTION) + expect(actual.countPerObjectType.collections).toBe(1) + expect(actual.countPerObjectType.datasets).toBe(1) + expect(actual.countPerObjectType.files).toBe(1) + + // Test with Filter query related to the collection + const collectionSearchCriteriaFilterQueryCollection = + new CollectionSearchCriteria().withFilterQueries(['dvCategory:Laboratory']) + + actual = await sut.getCollectionItems( + testCollectionAlias, + undefined, + undefined, + collectionSearchCriteriaFilterQueryCollection + ) + expect(actual.items.length).toBe(1) + expect(actual.totalItemCount).toBe(1) + expect((actual.items[0] as CollectionPreview).name).toBe(expectedCollectionsName) + expect(actual.facets).toEqual(expectedFacetsFromCollectionOnly) + expect(actual.countPerObjectType.collections).toBe(1) + expect(actual.countPerObjectType.datasets).toBe(0) + expect(actual.countPerObjectType.files).toBe(0) + + // Test with Filter query related to the dataset + const collectionSearchCriteriaFilterQueryDataset = + new CollectionSearchCriteria().withFilterQueries([ + 'subject_ss:Medicine, Health and Life Sciences' + ]) + + actual = await sut.getCollectionItems( + testCollectionAlias, + undefined, + undefined, + collectionSearchCriteriaFilterQueryDataset + ) + expect(actual.items.length).toBe(1) + expect(actual.totalItemCount).toBe(1) + expect((actual.items[0] as DatasetPreview).title).toBe(expectedDatasetDescription) + expect(actual.facets).toEqual(expectedFacetsFromDatasetOnly) + expect(actual.countPerObjectType.collections).toBe(0) + expect(actual.countPerObjectType.datasets).toBe(1) + expect(actual.countPerObjectType.files).toBe(0) + + // Test with Filter query related to the file + const collectionSearchCriteriaFilterQuerieCollAndFile = + new CollectionSearchCriteria().withFilterQueries(['fileAccess:Public']) + + actual = await sut.getCollectionItems( + testCollectionAlias, + undefined, + undefined, + collectionSearchCriteriaFilterQuerieCollAndFile + ) + + expect(actual.items.length).toBe(1) + expect(actual.totalItemCount).toBe(1) expect((actual.items[0] as FilePreview).name).toBe(expectedFileName) - expect((actual.items[1] as CollectionPreview).name).toBe(expectedCollectionsName) - expect(actual.countPerObjectType.collections).toBe(2) + expect(actual.facets).toEqual(expectedFacetsFromFileOnly) + expect(actual.countPerObjectType.collections).toBe(0) expect(actual.countPerObjectType.datasets).toBe(0) expect(actual.countPerObjectType.files).toBe(1) }) - test('should return error when role, type and publication status params are empty', async () => { - const expectedError = new ReadError('No results. Please select at least one Role.') + test('should return error when collection does not exist', async () => { + const expectedError = new ReadError( + `[400] Could not find dataverse with alias ${TestConstants.TEST_DUMMY_COLLECTION_ALIAS}` + ) await expect( - sut.getMyDataCollectionItems([], [], [], 0, 0, undefined, undefined) + sut.getCollectionItems(TestConstants.TEST_DUMMY_COLLECTION_ALIAS) ).rejects.toThrow(expectedError) }) }) - describe('getCollectionItems', () => { + describe('getCollectionItems for published tabular file', () => { let testDatasetIds: CreatedDatasetIdentifiers - - const testTextFile1Name = 'test-file-1.txt' + const testTextFile4Name = 'test-file-4.tab' const testSubCollectionAlias = 'collectionsRepositoryTestSubCollection' beforeAll(async () => { - await createCollectionViaApi(testSubCollectionAlias, testCollectionAlias).catch(() => { + await sut.publishCollection(testCollectionId).catch(() => { + throw new Error(`Tests beforeAll(): Error while publishing collection ${testCollectionId}`) + }) + + const collectionPayload = await createCollectionViaApi( + testSubCollectionAlias, + testCollectionAlias + ).catch(() => { throw new Error( `Tests beforeAll(): Error while creating subcollection ${testSubCollectionAlias}` ) }) + + await sut.publishCollection(collectionPayload.id).catch(() => { + throw new Error(`Tests beforeAll(): Error while publishing collection ${testCollectionId}`) + }) + try { testDatasetIds = await createDataset.execute( TestConstants.TEST_NEW_DATASET_DTO, @@ -727,17 +854,39 @@ describe('CollectionsRepository', () => { } catch (error) { throw new Error('Tests beforeAll(): Error while creating test dataset') } - await uploadFileViaApi(testDatasetIds.numericId, testTextFile1Name).catch(() => { - throw new Error(`Tests beforeAll(): Error while uploading file ${testTextFile1Name}`) + const uploadFileViaApiResult = await uploadFileViaApi( + testDatasetIds.numericId, + testTextFile4Name, + { + categories: ['tabular data'] + } + ).catch(() => { + throw new Error(`Tests beforeAll(): Error while uploading file ${testTextFile4Name}`) + }) + await new Promise((resolve) => setTimeout(resolve, 5000)) + + await updateFileTabularTags(uploadFileViaApiResult.data.data.files[0].dataFile.id, [ + 'Survey', + 'Genomics' + ]).catch(() => { + throw new Error( + `Tests beforeAll(): Error while updating file tabular tags ${uploadFileViaApiResult.data.data.files[0].dataFile.id}` + ) + }) + + await publishDatasetViaApi(testDatasetIds.numericId).catch(() => { + throw new Error( + `Tests beforeAll(): Error while publishing dataset ${testDatasetIds.numericId}` + ) }) }) afterAll(async () => { try { - await deleteUnpublishedDatasetViaApi(testDatasetIds.numericId) + await deletePublishedDatasetViaApi(testDatasetIds.persistentId) } catch (error) { throw new Error( - `Tests afterAll(): Error while deleting test dataset ${testDatasetIds.numericId}` + `Tests afterAll(): Error while deleting test dataset ${testDatasetIds.persistentId}` ) } try { @@ -753,78 +902,17 @@ describe('CollectionsRepository', () => { // Give enough time to Solr for indexing await new Promise((resolve) => setTimeout(resolve, 5000)) - let actual = await sut.getCollectionItems(testCollectionAlias) + const actual = await sut.getCollectionItems(testCollectionAlias) const actualFilePreview = actual.items[1] as FilePreview const actualDatasetPreview = actual.items[0] as DatasetPreview const actualCollectionPreview = actual.items[2] as CollectionPreview - const expectedFileMd5 = '68b22040025784da775f55cfcb6dee2e' - const expectedDatasetCitationFragment = `Admin, Dataverse; Owner, Dataverse, ${currentYear}, "Dataset created using the createDataset use case"` - const expectedDatasetDescription = 'Dataset created using the createDataset use case' - const expectedFileName = 'test-file-1.txt' - const expectedCollectionsName = 'Scientific Research' + const expectedFileMd5 = '77c7f03a7d7772907b43f0b322cef723' - //prettier-ignore - const expectedFacetsAll = [ - { - name: 'dvCategory', friendlyName: 'Dataverse Category', labels: [{ name: 'Laboratory', count: 1 }] - }, - { - name: 'publicationStatus', friendlyName: 'Publication Status', labels: [{ name: 'Unpublished', count: 3 },{ name: 'Draft', count: 2 }] - }, - { - name: 'authorName_ss', friendlyName: 'Author Name', labels: [{ name: 'Admin, Dataverse', count: 1 },{ name: 'Owner, Dataverse', count: 1 }] - }, - { - name: 'subject_ss', friendlyName: 'Subject', labels: [{ name: 'Medicine, Health and Life Sciences', count: 1 }] - }, - { - name: 'fileTypeGroupFacet', friendlyName: 'File Type', labels: [{ name: 'Text', count: 1 }] - }, - { - name: 'fileAccess', friendlyName: 'Access', labels: [{ name: 'Public', count: 1 }] - } - ] - //prettier-ignore - const expectedFacetsFromCollectionOnly = [ - { - name: 'dvCategory', friendlyName: 'Dataverse Category', labels: [{ name: 'Laboratory', count: 1 }] - }, - { - name: 'publicationStatus', friendlyName: 'Publication Status', labels: [{ name: 'Unpublished', count: 1 }] - } - ] - //prettier-ignore - const expectedFacetsFromDatasetOnly = [ - { - name: 'publicationStatus', friendlyName: 'Publication Status', labels: [{ name: 'Draft', count: 1 },{ name: 'Unpublished', count: 1 }] - }, - { - name: 'authorName_ss', friendlyName: 'Author Name', labels: [{ name: 'Admin, Dataverse', count: 1 },{ name: 'Owner, Dataverse', count: 1 }] - }, - { - name: 'subject_ss', friendlyName: 'Subject', labels: [{ name: 'Medicine, Health and Life Sciences', count: 1 }] - } - ] - //prettier-ignore - const expectedFacetsFromFileOnly = [ - { - name: 'publicationStatus', friendlyName: 'Publication Status', labels: [{ name: 'Draft', count: 1 },{ name: 'Unpublished', count: 1 }] - }, - { name: 'fileTypeGroupFacet', friendlyName: 'File Type', labels: [{ name: 'Text', count: 1 }] }, - { name: 'fileAccess', friendlyName: 'Access', labels: [{ name: 'Public', count: 1 }] } - ] - //prettier-ignore - const expectedFacetsFromCollectionAndFile = [ - { - name: 'dvCategory', friendlyName: 'Dataverse Category', labels: [{ name: 'Laboratory', count: 1 }] - }, - { - name: 'publicationStatus', friendlyName: 'Publication Status', labels: [{ name: 'Unpublished', count: 2 },{ name: 'Draft', count: 1 }] - }, - { name: 'fileTypeGroupFacet', friendlyName: 'File Type', labels: [{ name: 'Text', count: 1 }] }, - { name: 'fileAccess', friendlyName: 'Access', labels: [{ name: 'Public', count: 1 }] } - ] + const expectedDatasetCitationFragment = `Admin, Dataverse; Owner, Dataverse, ${currentYear}, "Dataset created using the createDataset use case` + const expectedDatasetDescription = 'Dataset created using the createDataset use case' + const expectedFileName = 'test-file-4.tab' + const expectedCollectionsName = 'Scientific Research' expect(actualFilePreview.checksum?.type).toBe('MD5') expect(actualFilePreview.checksum?.value).toBe(expectedFileMd5) @@ -833,33 +921,35 @@ describe('CollectionsRepository', () => { expect(actualFilePreview.datasetName).toBe(expectedDatasetDescription) expect(actualFilePreview.datasetPersistentId).toBe(testDatasetIds.persistentId) expect(actualFilePreview.description).toBe('') - expect(actualFilePreview.fileContentType).toBe('text/plain') + expect(actualFilePreview.fileContentType).toBe('text/tab-separated-values') expect(actualFilePreview.fileId).not.toBeUndefined() - expect(actualFilePreview.fileType).toBe('Plain Text') + expect(actualFilePreview.fileType).toBe('Tab-Delimited') expect(actualFilePreview.md5).toBe(expectedFileMd5) expect(actualFilePreview.name).toBe(expectedFileName) - expect(actualFilePreview.publicationStatuses[0]).toBe(PublicationStatus.Unpublished) - expect(actualFilePreview.publicationStatuses[1]).toBe(PublicationStatus.Draft) - expect(actualFilePreview.sizeInBytes).toBe(12) + expect(actualFilePreview.publicationStatuses[0]).toBe(PublicationStatus.Published) + expect(actualFilePreview.sizeInBytes).toBe(137) expect(actualFilePreview.url).not.toBeUndefined() expect(actualFilePreview.releaseOrCreateDate).not.toBeUndefined() expect(actualFilePreview.type).toBe(CollectionItemType.FILE) expect(actualFilePreview.restricted).toBe(false) expect(actualFilePreview.canDownloadFile).toBe(true) + expect(actualFilePreview.categories).toEqual(['tabular data']) + expect(actualFilePreview.tabularTags).toEqual(['Genomics', 'Survey']) + expect(actualFilePreview.observations).toBe(10) + expect(actualFilePreview.variables).toBe(3) expect(actualDatasetPreview.title).toBe(expectedDatasetDescription) expect(actualDatasetPreview.citation).toContain(expectedDatasetCitationFragment) expect(actualDatasetPreview.description).toBe('This is the description of the dataset.') expect(actualDatasetPreview.persistentId).not.toBeUndefined() expect(actualDatasetPreview.persistentId).not.toBeUndefined() - expect(actualDatasetPreview.publicationStatuses[0]).toBe(PublicationStatus.Unpublished) - expect(actualDatasetPreview.publicationStatuses[1]).toBe(PublicationStatus.Draft) + expect(actualDatasetPreview.publicationStatuses[0]).toBe(PublicationStatus.Published) expect(actualDatasetPreview.versionId).not.toBeUndefined() expect(actualDatasetPreview.versionInfo.createTime).not.toBeUndefined() expect(actualDatasetPreview.versionInfo.lastUpdateTime).not.toBeUndefined() - expect(actualDatasetPreview.versionInfo.majorNumber).toBeUndefined() - expect(actualDatasetPreview.versionInfo.minorNumber).toBeUndefined() - expect(actualDatasetPreview.versionInfo.state).toBe('DRAFT') + expect(actualDatasetPreview.versionInfo.majorNumber).toBe(1) + expect(actualDatasetPreview.versionInfo.minorNumber).toBe(0) + expect(actualDatasetPreview.versionInfo.state).toBe('RELEASED') expect(actualDatasetPreview.parentCollectionAlias).toBe( 'collectionsRepositoryTestSubCollection' ) @@ -872,7 +962,7 @@ describe('CollectionsRepository', () => { expect(actualCollectionPreview.imageUrl).toBe(undefined) expect(actualCollectionPreview.parentAlias).toBe(testCollectionAlias) expect(actualCollectionPreview.parentName).toBe(expectedCollectionsName) - expect(actualCollectionPreview.publicationStatuses[0]).toBe(PublicationStatus.Unpublished) + expect(actualCollectionPreview.publicationStatuses[0]).toBe(PublicationStatus.Published) expect(actualCollectionPreview.releaseOrCreateDate).not.toBeUndefined() expect(actualCollectionPreview.affiliation).toBe('Scientific Research University') expect(actualCollectionPreview.parentAlias).toBe('collectionsRepositoryTestCollection') @@ -880,585 +970,144 @@ describe('CollectionsRepository', () => { expect(actualCollectionPreview.type).toBe(CollectionItemType.COLLECTION) expect(actual.totalItemCount).toBe(3) - expect(actual.countPerObjectType.collections).toBe(1) - expect(actual.countPerObjectType.datasets).toBe(1) - expect(actual.countPerObjectType.files).toBe(1) + }) + }) - expect(actual.facets).toEqual(expectedFacetsAll) + describe('updateCollection', () => { + const testUpdatedCollectionAlias = 'updateCollection-test-updatedAlias' - // Test limit and offset - actual = await sut.getCollectionItems(testCollectionAlias, 1, 1) - expect((actual.items[0] as FilePreview).name).toBe(expectedFileName) - expect(actual.items.length).toBe(1) - expect(actual.totalItemCount).toBe(3) + afterAll(async () => { + await deleteCollectionViaApi(testUpdatedCollectionAlias) + }) - // Test search text - const collectionSearchCriteriaForFile = new CollectionSearchCriteria().withSearchText( - 'test-fi' - ) - actual = await sut.getCollectionItems( - testCollectionAlias, - undefined, - undefined, - collectionSearchCriteriaForFile - ) - expect(actual.totalItemCount).toBe(1) - expect((actual.items[0] as FilePreview).name).toBe(expectedFileName) - expect(actual.countPerObjectType.collections).toBe(0) - expect(actual.countPerObjectType.datasets).toBe(0) - expect(actual.countPerObjectType.files).toBe(1) + test('should update the collection', async () => { + // First we create a test collection using a CollectionDTO and createCollection method + const collectionDTO = createCollectionDTO('updatedCollection-test-originalAlias') + const testUpdateCollectionId = await sut.createCollection(collectionDTO) + const createdCollection = await sut.getCollection(testUpdateCollectionId) + expect(createdCollection.id).toBe(testUpdateCollectionId) + expect(createdCollection.alias).toBe(collectionDTO.alias) + expect(createdCollection.name).toBe(collectionDTO.name) + expect(createdCollection.affiliation).toBe(collectionDTO.affiliation) + expect(createdCollection.inputLevels?.length).toBe(1) + const inputLevel = createdCollection.inputLevels?.[0] + expect(inputLevel?.datasetFieldName).toBe('geographicCoverage') + expect(inputLevel?.include).toBe(true) + expect(inputLevel?.required).toBe(true) - const collectionSearchCriteriaForDataset = new CollectionSearchCriteria().withSearchText( - 'This is the description' - ) - actual = await sut.getCollectionItems( - testCollectionAlias, - undefined, - undefined, - collectionSearchCriteriaForDataset - ) - expect(actual.totalItemCount).toBe(1) - expect((actual.items[0] as DatasetPreview).title).toBe(expectedDatasetDescription) - expect(actual.countPerObjectType.collections).toBe(0) - expect(actual.countPerObjectType.datasets).toBe(1) - expect(actual.countPerObjectType.files).toBe(0) + // Now we update CollectionDTO and verify updates are correctly persisted after calling updateCollection method + collectionDTO.alias = testUpdatedCollectionAlias + const updatedCollectionName = 'updatedCollectionName' + collectionDTO.name = updatedCollectionName + const updatedCollectionAffiliation = 'updatedCollectionAffiliation' + collectionDTO.affiliation = updatedCollectionAffiliation + const updatedInputLevels = [ + { + datasetFieldName: 'country', + required: false, + include: true + } + ] + collectionDTO.inputLevels = updatedInputLevels + await sut.updateCollection(testUpdateCollectionId, collectionDTO) + const updatedCollection = await sut.getCollection(testUpdateCollectionId) + expect(updatedCollection.id).toBe(testUpdateCollectionId) + expect(updatedCollection.alias).toBe(testUpdatedCollectionAlias) + expect(updatedCollection.name).toBe(updatedCollectionName) + expect(updatedCollection.affiliation).toBe(updatedCollectionAffiliation) + expect(updatedCollection.inputLevels?.length).toBe(1) + const updatedInputLevel = updatedCollection.inputLevels?.[0] + expect(updatedInputLevel?.datasetFieldName).toBe('country') + expect(updatedInputLevel?.include).toBe(true) + expect(updatedInputLevel?.required).toBe(false) + }) - const collectionSearchCriteriaForDatasetAndCollection = - new CollectionSearchCriteria().withSearchText('the') - actual = await sut.getCollectionItems( - testCollectionAlias, - undefined, - undefined, - collectionSearchCriteriaForDatasetAndCollection - ) - expect(actual.totalItemCount).toBe(2) - expect((actual.items[0] as DatasetPreview).title).toBe(expectedDatasetDescription) - expect((actual.items[1] as CollectionPreview).name).toBe(expectedCollectionsName) - expect(actual.countPerObjectType.collections).toBe(1) - expect(actual.countPerObjectType.datasets).toBe(1) - expect(actual.countPerObjectType.files).toBe(0) + test('should update the collection to inherit metadata blocks from parent collection', async () => { + const parentCollectionAlias = 'inherit-metablocks-parent-update' + const parentCollectionDTO = createCollectionDTO(parentCollectionAlias) + const parentCollectionId = await sut.createCollection(parentCollectionDTO) - // Test search text, limit and offset - actual = await sut.getCollectionItems( - testCollectionAlias, - 1, - 1, - collectionSearchCriteriaForDatasetAndCollection - ) - expect(actual.items.length).toBe(1) - expect(actual.totalItemCount).toBe(2) - expect((actual.items[0] as CollectionPreview).name).toBe(expectedCollectionsName) - expect(actual.countPerObjectType.collections).toBe(1) - expect(actual.countPerObjectType.datasets).toBe(1) - expect(actual.countPerObjectType.files).toBe(0) + const childCollectionAlias = 'inherit-metablocks-child-update' + const childCollectionDTO = createCollectionDTO(childCollectionAlias) - // Test type collection - const collectionSearchCriteriaForCollectionType = - new CollectionSearchCriteria().withItemTypes([CollectionItemType.COLLECTION]) - actual = await sut.getCollectionItems( - testCollectionAlias, - undefined, - undefined, - collectionSearchCriteriaForCollectionType - ) - expect(actual.items.length).toBe(1) - expect(actual.totalItemCount).toBe(1) - expect((actual.items[0] as CollectionPreview).name).toBe(expectedCollectionsName) - expect(actual.facets).toEqual(expectedFacetsFromCollectionOnly) - expect(actual.countPerObjectType.collections).toBe(1) - expect(actual.countPerObjectType.datasets).toBe(1) - expect(actual.countPerObjectType.files).toBe(1) + const childCollectionId = await sut.createCollection(childCollectionDTO, parentCollectionId) - // Test type dataset - const collectionSearchCriteriaForDatasetType = new CollectionSearchCriteria().withItemTypes([ - CollectionItemType.DATASET - ]) - actual = await sut.getCollectionItems( - testCollectionAlias, - undefined, - undefined, - collectionSearchCriteriaForDatasetType - ) - expect(actual.items.length).toBe(1) - expect(actual.totalItemCount).toBe(1) - expect((actual.items[0] as DatasetPreview).title).toBe(expectedDatasetDescription) - expect(actual.facets).toEqual(expectedFacetsFromDatasetOnly) - expect(actual.countPerObjectType.collections).toBe(1) - expect(actual.countPerObjectType.datasets).toBe(1) - expect(actual.countPerObjectType.files).toBe(1) + const childCollection = await sut.getCollection(childCollectionId) - // Test type file - const collectionSearchCriteriaForFileType = new CollectionSearchCriteria().withItemTypes([ - CollectionItemType.FILE - ]) - actual = await sut.getCollectionItems( - testCollectionAlias, - undefined, - undefined, - collectionSearchCriteriaForFileType - ) - expect(actual.items.length).toBe(1) - expect(actual.totalItemCount).toBe(1) - expect((actual.items[0] as FilePreview).name).toBe(expectedFileName) - expect(actual.facets).toEqual(expectedFacetsFromFileOnly) - expect(actual.countPerObjectType.collections).toBe(1) - expect(actual.countPerObjectType.datasets).toBe(1) - expect(actual.countPerObjectType.files).toBe(1) - - // Test multiple types - const collectionSearchCriteriaForMultiTypes = new CollectionSearchCriteria().withItemTypes([ - CollectionItemType.FILE, - CollectionItemType.COLLECTION - ]) - actual = await sut.getCollectionItems( - testCollectionAlias, - undefined, - undefined, - collectionSearchCriteriaForMultiTypes - ) - expect(actual.items.length).toBe(2) - expect(actual.totalItemCount).toBe(2) - expect((actual.items[0] as FilePreview).name).toBe(expectedFileName) - expect((actual.items[1] as CollectionPreview).name).toBe(expectedCollectionsName) - expect(actual.facets).toEqual(expectedFacetsFromCollectionAndFile) - expect(actual.countPerObjectType.collections).toBe(1) - expect(actual.countPerObjectType.datasets).toBe(1) - expect(actual.countPerObjectType.files).toBe(1) + expect(childCollection.isMetadataBlockRoot).toBe(true) + expect(childCollection.isFacetRoot).toBe(true) - // Test Sort by name ascending - const collectionSearchCriteriaNameAscending = new CollectionSearchCriteria() - .withSort(SortType.NAME) - .withOrder(OrderType.ASC) + const updatedChildCollectionDTO = createCollectionDTO(childCollectionAlias) + updatedChildCollectionDTO.inheritMetadataBlocksFromParent = true - actual = await sut.getCollectionItems( - testCollectionAlias, - undefined, - undefined, - collectionSearchCriteriaNameAscending - ) - expect(actual.items.length).toBe(3) - expect(actual.totalItemCount).toBe(3) - expect((actual.items[0] as DatasetPreview).type).toBe(CollectionItemType.DATASET) - expect((actual.items[1] as CollectionPreview).type).toBe(CollectionItemType.COLLECTION) - expect((actual.items[2] as FilePreview).type).toBe(CollectionItemType.FILE) - expect(actual.countPerObjectType.collections).toBe(1) - expect(actual.countPerObjectType.datasets).toBe(1) - expect(actual.countPerObjectType.files).toBe(1) + await sut.updateCollection(childCollectionId, updatedChildCollectionDTO) - // Test Sort by name descending - const collectionSearchCriteriaNameDescending = new CollectionSearchCriteria() - .withSort(SortType.NAME) - .withOrder(OrderType.DESC) + const childCollectionAfterUpdate = await sut.getCollection(childCollectionId) - actual = await sut.getCollectionItems( - testCollectionAlias, - undefined, - undefined, - collectionSearchCriteriaNameDescending - ) - expect(actual.items.length).toBe(3) - expect(actual.totalItemCount).toBe(3) - expect((actual.items[0] as FilePreview).type).toBe(CollectionItemType.FILE) - expect((actual.items[1] as CollectionPreview).type).toBe(CollectionItemType.COLLECTION) - expect((actual.items[2] as DatasetPreview).type).toBe(CollectionItemType.DATASET) - expect(actual.countPerObjectType.collections).toBe(1) - expect(actual.countPerObjectType.datasets).toBe(1) - expect(actual.countPerObjectType.files).toBe(1) + expect(childCollectionAfterUpdate.isMetadataBlockRoot).toBe(false) + expect(childCollectionAfterUpdate.isFacetRoot).toBe(true) - // Test Sort by date ascending - const collectionSearchCriteriaDateAscending = new CollectionSearchCriteria() - .withSort(SortType.DATE) - .withOrder(OrderType.ASC) + await deleteCollectionViaApi(childCollectionAlias) + await deleteCollectionViaApi(parentCollectionAlias) + }) - actual = await sut.getCollectionItems( - testCollectionAlias, - undefined, - undefined, - collectionSearchCriteriaDateAscending - ) - expect(actual.items.length).toBe(3) - expect(actual.totalItemCount).toBe(3) - expect((actual.items[0] as CollectionPreview).type).toBe(CollectionItemType.COLLECTION) - expect((actual.items[1] as DatasetPreview).type).toBe(CollectionItemType.DATASET) - expect((actual.items[2] as FilePreview).type).toBe(CollectionItemType.FILE) - expect(actual.countPerObjectType.collections).toBe(1) - expect(actual.countPerObjectType.datasets).toBe(1) - expect(actual.countPerObjectType.files).toBe(1) + test('should update the collection to inherit facets from parent collection', async () => { + const parentCollectionAlias = 'inherit-facets-parent-update' + const parentCollectionDTO = createCollectionDTO(parentCollectionAlias) + const parentCollectionId = await sut.createCollection(parentCollectionDTO) - // Test Sort by date descending - const collectionSearchCriteriaDateDescending = new CollectionSearchCriteria() - .withSort(SortType.DATE) - .withOrder(OrderType.DESC) + const childCollectionAlias = 'inherit-facets-child-update' + const childCollectionDTO = createCollectionDTO(childCollectionAlias) - actual = await sut.getCollectionItems( - testCollectionAlias, - undefined, - undefined, - collectionSearchCriteriaDateDescending - ) - expect(actual.items.length).toBe(3) - expect(actual.totalItemCount).toBe(3) - expect((actual.items[0] as DatasetPreview).type).toBe(CollectionItemType.DATASET) - expect((actual.items[1] as FilePreview).type).toBe(CollectionItemType.FILE) - expect((actual.items[2] as CollectionPreview).type).toBe(CollectionItemType.COLLECTION) - expect(actual.countPerObjectType.collections).toBe(1) - expect(actual.countPerObjectType.datasets).toBe(1) - expect(actual.countPerObjectType.files).toBe(1) + const childCollectionId = await sut.createCollection(childCollectionDTO, parentCollectionId) - // Test with Filter query related to the collection - const collectionSearchCriteriaFilterQueryCollection = - new CollectionSearchCriteria().withFilterQueries(['dvCategory:Laboratory']) + const childCollection = await sut.getCollection(childCollectionId) - actual = await sut.getCollectionItems( - testCollectionAlias, - undefined, - undefined, - collectionSearchCriteriaFilterQueryCollection - ) - expect(actual.items.length).toBe(1) - expect(actual.totalItemCount).toBe(1) - expect((actual.items[0] as CollectionPreview).name).toBe(expectedCollectionsName) - expect(actual.facets).toEqual(expectedFacetsFromCollectionOnly) - expect(actual.countPerObjectType.collections).toBe(1) - expect(actual.countPerObjectType.datasets).toBe(0) - expect(actual.countPerObjectType.files).toBe(0) + expect(childCollection.isMetadataBlockRoot).toBe(true) + expect(childCollection.isFacetRoot).toBe(true) - // Test with Filter query related to the dataset - const collectionSearchCriteriaFilterQueryDataset = - new CollectionSearchCriteria().withFilterQueries([ - 'subject_ss:Medicine, Health and Life Sciences' - ]) + const updatedChildCollectionDTO = createCollectionDTO(childCollectionAlias) + updatedChildCollectionDTO.inheritFacetsFromParent = true - actual = await sut.getCollectionItems( - testCollectionAlias, - undefined, - undefined, - collectionSearchCriteriaFilterQueryDataset - ) - expect(actual.items.length).toBe(1) - expect(actual.totalItemCount).toBe(1) - expect((actual.items[0] as DatasetPreview).title).toBe(expectedDatasetDescription) - expect(actual.facets).toEqual(expectedFacetsFromDatasetOnly) - expect(actual.countPerObjectType.collections).toBe(0) - expect(actual.countPerObjectType.datasets).toBe(1) - expect(actual.countPerObjectType.files).toBe(0) + await sut.updateCollection(childCollectionId, updatedChildCollectionDTO) - // Test with Filter query related to the file - const collectionSearchCriteriaFilterQuerieCollAndFile = - new CollectionSearchCriteria().withFilterQueries(['fileAccess:Public']) + const childCollectionAfterUpdate = await sut.getCollection(childCollectionId) - actual = await sut.getCollectionItems( - testCollectionAlias, - undefined, - undefined, - collectionSearchCriteriaFilterQuerieCollAndFile - ) + expect(childCollectionAfterUpdate.isMetadataBlockRoot).toBe(true) + expect(childCollectionAfterUpdate.isFacetRoot).toBe(false) - expect(actual.items.length).toBe(1) - expect(actual.totalItemCount).toBe(1) - expect((actual.items[0] as FilePreview).name).toBe(expectedFileName) - expect(actual.facets).toEqual(expectedFacetsFromFileOnly) - expect(actual.countPerObjectType.collections).toBe(0) - expect(actual.countPerObjectType.datasets).toBe(0) - expect(actual.countPerObjectType.files).toBe(1) + await deleteCollectionViaApi(childCollectionAlias) + await deleteCollectionViaApi(parentCollectionAlias) }) - test('should return error when collection does not exist', async () => { - const expectedError = new ReadError( - `[400] Could not find dataverse with alias ${TestConstants.TEST_DUMMY_COLLECTION_ALIAS}` - ) + test('should update the collection to inherit metadata blocks and facets from parent collection', async () => { + const parentCollectionAlias = 'inherit-metablocks-facets-parent-update' + const parentCollectionDTO = createCollectionDTO(parentCollectionAlias) + const parentCollectionId = await sut.createCollection(parentCollectionDTO) - await expect( - sut.getCollectionItems(TestConstants.TEST_DUMMY_COLLECTION_ALIAS) - ).rejects.toThrow(expectedError) - }) - }) + const childCollectionAlias = 'inherit-metablocks-facets-child-update' + const childCollectionDTO = createCollectionDTO(childCollectionAlias) - describe('getCollectionItems for published tabular file', () => { - let testDatasetIds: CreatedDatasetIdentifiers - const testTextFile4Name = 'test-file-4.tab' - const testSubCollectionAlias = 'collectionsRepositoryTestSubCollection' + const childCollectionId = await sut.createCollection(childCollectionDTO, parentCollectionId) - beforeAll(async () => { - await sut.publishCollection(testCollectionId).catch(() => { - throw new Error(`Tests beforeAll(): Error while publishing collection ${testCollectionId}`) - }) + const childCollection = await sut.getCollection(childCollectionId) - const collectionPayload = await createCollectionViaApi( - testSubCollectionAlias, - testCollectionAlias - ).catch(() => { - throw new Error( - `Tests beforeAll(): Error while creating subcollection ${testSubCollectionAlias}` - ) - }) + expect(childCollection.isMetadataBlockRoot).toBe(true) + expect(childCollection.isFacetRoot).toBe(true) - await sut.publishCollection(collectionPayload.id).catch(() => { - throw new Error(`Tests beforeAll(): Error while publishing collection ${testCollectionId}`) - }) + const updatedChildCollectionDTO = createCollectionDTO(childCollectionAlias) + updatedChildCollectionDTO.inheritFacetsFromParent = true + updatedChildCollectionDTO.inheritMetadataBlocksFromParent = true - try { - testDatasetIds = await createDataset.execute( - TestConstants.TEST_NEW_DATASET_DTO, - testSubCollectionAlias - ) - } catch (error) { - throw new Error('Tests beforeAll(): Error while creating test dataset') - } - const uploadFileViaApiResult = await uploadFileViaApi( - testDatasetIds.numericId, - testTextFile4Name, - { - categories: ['tabular data'] - } - ).catch(() => { - throw new Error(`Tests beforeAll(): Error while uploading file ${testTextFile4Name}`) - }) - await new Promise((resolve) => setTimeout(resolve, 5000)) + await sut.updateCollection(childCollectionId, updatedChildCollectionDTO) - await updateFileTabularTags(uploadFileViaApiResult.data.data.files[0].dataFile.id, [ - 'Survey', - 'Genomics' - ]).catch(() => { - throw new Error( - `Tests beforeAll(): Error while updating file tabular tags ${uploadFileViaApiResult.data.data.files[0].dataFile.id}` - ) - }) + const childCollectionAfterUpdate = await sut.getCollection(childCollectionId) - await publishDatasetViaApi(testDatasetIds.numericId).catch(() => { - throw new Error( - `Tests beforeAll(): Error while publishing dataset ${testDatasetIds.numericId}` - ) - }) - }) - - afterAll(async () => { - try { - await deletePublishedDatasetViaApi(testDatasetIds.persistentId) - } catch (error) { - throw new Error( - `Tests afterAll(): Error while deleting test dataset ${testDatasetIds.persistentId}` - ) - } - try { - await deleteCollectionViaApi(testSubCollectionAlias) - } catch (error) { - throw new Error( - `Tests afterAll(): Error while deleting subcollection ${testSubCollectionAlias}` - ) - } - }) - - test('should return collection items given a valid collection alias', async () => { - // Give enough time to Solr for indexing - await new Promise((resolve) => setTimeout(resolve, 5000)) - - const actual = await sut.getCollectionItems(testCollectionAlias) - const actualFilePreview = actual.items[1] as FilePreview - const actualDatasetPreview = actual.items[0] as DatasetPreview - const actualCollectionPreview = actual.items[2] as CollectionPreview - - const expectedFileMd5 = '77c7f03a7d7772907b43f0b322cef723' - - const expectedDatasetCitationFragment = `Admin, Dataverse; Owner, Dataverse, ${currentYear}, "Dataset created using the createDataset use case` - const expectedDatasetDescription = 'Dataset created using the createDataset use case' - const expectedFileName = 'test-file-4.tab' - const expectedCollectionsName = 'Scientific Research' - - expect(actualFilePreview.checksum?.type).toBe('MD5') - expect(actualFilePreview.checksum?.value).toBe(expectedFileMd5) - expect(actualFilePreview.datasetCitation).toContain(expectedDatasetCitationFragment) - expect(actualFilePreview.datasetId).toBe(testDatasetIds.numericId) - expect(actualFilePreview.datasetName).toBe(expectedDatasetDescription) - expect(actualFilePreview.datasetPersistentId).toBe(testDatasetIds.persistentId) - expect(actualFilePreview.description).toBe('') - expect(actualFilePreview.fileContentType).toBe('text/tab-separated-values') - expect(actualFilePreview.fileId).not.toBeUndefined() - expect(actualFilePreview.fileType).toBe('Tab-Delimited') - expect(actualFilePreview.md5).toBe(expectedFileMd5) - expect(actualFilePreview.name).toBe(expectedFileName) - expect(actualFilePreview.publicationStatuses[0]).toBe(PublicationStatus.Published) - expect(actualFilePreview.sizeInBytes).toBe(137) - expect(actualFilePreview.url).not.toBeUndefined() - expect(actualFilePreview.releaseOrCreateDate).not.toBeUndefined() - expect(actualFilePreview.type).toBe(CollectionItemType.FILE) - expect(actualFilePreview.restricted).toBe(false) - expect(actualFilePreview.canDownloadFile).toBe(true) - expect(actualFilePreview.categories).toEqual(['tabular data']) - expect(actualFilePreview.tabularTags).toEqual(['Genomics', 'Survey']) - expect(actualFilePreview.observations).toBe(10) - expect(actualFilePreview.variables).toBe(3) - - expect(actualDatasetPreview.title).toBe(expectedDatasetDescription) - expect(actualDatasetPreview.citation).toContain(expectedDatasetCitationFragment) - expect(actualDatasetPreview.description).toBe('This is the description of the dataset.') - expect(actualDatasetPreview.persistentId).not.toBeUndefined() - expect(actualDatasetPreview.persistentId).not.toBeUndefined() - expect(actualDatasetPreview.publicationStatuses[0]).toBe(PublicationStatus.Published) - expect(actualDatasetPreview.versionId).not.toBeUndefined() - expect(actualDatasetPreview.versionInfo.createTime).not.toBeUndefined() - expect(actualDatasetPreview.versionInfo.lastUpdateTime).not.toBeUndefined() - expect(actualDatasetPreview.versionInfo.majorNumber).toBe(1) - expect(actualDatasetPreview.versionInfo.minorNumber).toBe(0) - expect(actualDatasetPreview.versionInfo.state).toBe('RELEASED') - expect(actualDatasetPreview.parentCollectionAlias).toBe( - 'collectionsRepositoryTestSubCollection' - ) - expect(actualDatasetPreview.parentCollectionName).toBe(expectedCollectionsName) - expect(actualDatasetPreview.type).toBe(CollectionItemType.DATASET) - - expect(actualCollectionPreview.name).toBe(expectedCollectionsName) - expect(actualCollectionPreview.alias).toBe(testSubCollectionAlias) - expect(actualCollectionPreview.description).toBe('We do all the science.') - expect(actualCollectionPreview.imageUrl).toBe(undefined) - expect(actualCollectionPreview.parentAlias).toBe(testCollectionAlias) - expect(actualCollectionPreview.parentName).toBe(expectedCollectionsName) - expect(actualCollectionPreview.publicationStatuses[0]).toBe(PublicationStatus.Published) - expect(actualCollectionPreview.releaseOrCreateDate).not.toBeUndefined() - expect(actualCollectionPreview.affiliation).toBe('Scientific Research University') - expect(actualCollectionPreview.parentAlias).toBe('collectionsRepositoryTestCollection') - expect(actualCollectionPreview.parentName).toBe(expectedCollectionsName) - expect(actualCollectionPreview.type).toBe(CollectionItemType.COLLECTION) - - expect(actual.totalItemCount).toBe(3) - }) - }) - - describe('updateCollection', () => { - const testUpdatedCollectionAlias = 'updateCollection-test-updatedAlias' - - afterAll(async () => { - await deleteCollectionViaApi(testUpdatedCollectionAlias) - }) - - test('should update the collection', async () => { - // First we create a test collection using a CollectionDTO and createCollection method - const collectionDTO = createCollectionDTO('updatedCollection-test-originalAlias') - const testUpdateCollectionId = await sut.createCollection(collectionDTO) - const createdCollection = await sut.getCollection(testUpdateCollectionId) - expect(createdCollection.id).toBe(testUpdateCollectionId) - expect(createdCollection.alias).toBe(collectionDTO.alias) - expect(createdCollection.name).toBe(collectionDTO.name) - expect(createdCollection.affiliation).toBe(collectionDTO.affiliation) - expect(createdCollection.inputLevels?.length).toBe(1) - const inputLevel = createdCollection.inputLevels?.[0] - expect(inputLevel?.datasetFieldName).toBe('geographicCoverage') - expect(inputLevel?.include).toBe(true) - expect(inputLevel?.required).toBe(true) - - // Now we update CollectionDTO and verify updates are correctly persisted after calling updateCollection method - collectionDTO.alias = testUpdatedCollectionAlias - const updatedCollectionName = 'updatedCollectionName' - collectionDTO.name = updatedCollectionName - const updatedCollectionAffiliation = 'updatedCollectionAffiliation' - collectionDTO.affiliation = updatedCollectionAffiliation - const updatedInputLevels = [ - { - datasetFieldName: 'country', - required: false, - include: true - } - ] - collectionDTO.inputLevels = updatedInputLevels - await sut.updateCollection(testUpdateCollectionId, collectionDTO) - const updatedCollection = await sut.getCollection(testUpdateCollectionId) - expect(updatedCollection.id).toBe(testUpdateCollectionId) - expect(updatedCollection.alias).toBe(testUpdatedCollectionAlias) - expect(updatedCollection.name).toBe(updatedCollectionName) - expect(updatedCollection.affiliation).toBe(updatedCollectionAffiliation) - expect(updatedCollection.inputLevels?.length).toBe(1) - const updatedInputLevel = updatedCollection.inputLevels?.[0] - expect(updatedInputLevel?.datasetFieldName).toBe('country') - expect(updatedInputLevel?.include).toBe(true) - expect(updatedInputLevel?.required).toBe(false) - }) - - test('should update the collection to inherit metadata blocks from parent collection', async () => { - const parentCollectionAlias = 'inherit-metablocks-parent-update' - const parentCollectionDTO = createCollectionDTO(parentCollectionAlias) - const parentCollectionId = await sut.createCollection(parentCollectionDTO) - - const childCollectionAlias = 'inherit-metablocks-child-update' - const childCollectionDTO = createCollectionDTO(childCollectionAlias) - - const childCollectionId = await sut.createCollection(childCollectionDTO, parentCollectionId) - - const childCollection = await sut.getCollection(childCollectionId) - - expect(childCollection.isMetadataBlockRoot).toBe(true) - expect(childCollection.isFacetRoot).toBe(true) - - const updatedChildCollectionDTO = createCollectionDTO(childCollectionAlias) - updatedChildCollectionDTO.inheritMetadataBlocksFromParent = true - - await sut.updateCollection(childCollectionId, updatedChildCollectionDTO) - - const childCollectionAfterUpdate = await sut.getCollection(childCollectionId) - - expect(childCollectionAfterUpdate.isMetadataBlockRoot).toBe(false) - expect(childCollectionAfterUpdate.isFacetRoot).toBe(true) - - await deleteCollectionViaApi(childCollectionAlias) - await deleteCollectionViaApi(parentCollectionAlias) - }) - - test('should update the collection to inherit facets from parent collection', async () => { - const parentCollectionAlias = 'inherit-facets-parent-update' - const parentCollectionDTO = createCollectionDTO(parentCollectionAlias) - const parentCollectionId = await sut.createCollection(parentCollectionDTO) - - const childCollectionAlias = 'inherit-facets-child-update' - const childCollectionDTO = createCollectionDTO(childCollectionAlias) - - const childCollectionId = await sut.createCollection(childCollectionDTO, parentCollectionId) - - const childCollection = await sut.getCollection(childCollectionId) - - expect(childCollection.isMetadataBlockRoot).toBe(true) - expect(childCollection.isFacetRoot).toBe(true) - - const updatedChildCollectionDTO = createCollectionDTO(childCollectionAlias) - updatedChildCollectionDTO.inheritFacetsFromParent = true - - await sut.updateCollection(childCollectionId, updatedChildCollectionDTO) - - const childCollectionAfterUpdate = await sut.getCollection(childCollectionId) - - expect(childCollectionAfterUpdate.isMetadataBlockRoot).toBe(true) - expect(childCollectionAfterUpdate.isFacetRoot).toBe(false) - - await deleteCollectionViaApi(childCollectionAlias) - await deleteCollectionViaApi(parentCollectionAlias) - }) - - test('should update the collection to inherit metadata blocks and facets from parent collection', async () => { - const parentCollectionAlias = 'inherit-metablocks-facets-parent-update' - const parentCollectionDTO = createCollectionDTO(parentCollectionAlias) - const parentCollectionId = await sut.createCollection(parentCollectionDTO) - - const childCollectionAlias = 'inherit-metablocks-facets-child-update' - const childCollectionDTO = createCollectionDTO(childCollectionAlias) - - const childCollectionId = await sut.createCollection(childCollectionDTO, parentCollectionId) - - const childCollection = await sut.getCollection(childCollectionId) - - expect(childCollection.isMetadataBlockRoot).toBe(true) - expect(childCollection.isFacetRoot).toBe(true) - - const updatedChildCollectionDTO = createCollectionDTO(childCollectionAlias) - updatedChildCollectionDTO.inheritFacetsFromParent = true - updatedChildCollectionDTO.inheritMetadataBlocksFromParent = true - - await sut.updateCollection(childCollectionId, updatedChildCollectionDTO) - - const childCollectionAfterUpdate = await sut.getCollection(childCollectionId) - - expect(childCollectionAfterUpdate.isMetadataBlockRoot).toBe(false) - expect(childCollectionAfterUpdate.isFacetRoot).toBe(false) - - await deleteCollectionViaApi(childCollectionAlias) - await deleteCollectionViaApi(parentCollectionAlias) + expect(childCollectionAfterUpdate.isMetadataBlockRoot).toBe(false) + expect(childCollectionAfterUpdate.isFacetRoot).toBe(false) + + await deleteCollectionViaApi(childCollectionAlias) + await deleteCollectionViaApi(parentCollectionAlias) }) test('should not update root collection facets and keep isMetadataBlockRoot and isFacetRoot in true if facet ids are sent as undefined', async () => { @@ -1677,4 +1326,379 @@ describe('CollectionsRepository', () => { expect(featuredItemsResponseAfterDeletion).toStrictEqual([]) }) }) + describe('getMyDataCollectionItems', () => { + let testDatasetIds: CreatedDatasetIdentifiers + + const testTextFile1Name = 'test-file-2.txt' + const testSubCollectionAlias = 'collectionsRepositoryMyDataCollection' + beforeAll(async () => { + const myDataUserApiToken = await createApiTokenViaApi('myDataUser') + ApiConfig.init( + TestConstants.TEST_API_URL, + DataverseApiAuthMechanism.API_KEY, + myDataUserApiToken + ) + process.env.TEST_API_KEY = myDataUserApiToken + + await createCollectionViaApi(testSubCollectionAlias).catch((error) => { + console.log(error.message) + throw new Error( + `Tests beforeAll(): Error while creating subcollection ${testSubCollectionAlias}` + ) + }) + try { + testDatasetIds = await createDataset.execute( + TestConstants.TEST_NEW_DATASET_DTO, + testSubCollectionAlias + ) + } catch (error: any) { + console.log(error.message) + throw new Error('Tests beforeAll(): Error while creating test dataset') + } + await uploadFileViaApi(testDatasetIds.numericId, testTextFile1Name).catch(() => { + throw new Error(`Tests beforeAll(): Error while uploading file ${testTextFile1Name}`) + }) + }) + + afterAll(async () => { + try { + await deleteUnpublishedDatasetViaApi(testDatasetIds.numericId) + } catch (error) { + throw new Error( + `Tests afterAll(): Error while deleting test dataset ${testDatasetIds.numericId}` + ) + } + try { + await deleteCollectionViaApi(testSubCollectionAlias) + } catch (error) { + throw new Error( + `Tests afterAll(): Error while deleting subcollection ${testSubCollectionAlias}` + ) + } + }) + // TODO: remove this skip when the test is fixed + test.skip('should return collection items given valid roleIds', async () => { + // Give enough time to Solr for indexing + await new Promise((resolve) => setTimeout(resolve, 5000)) + await getCollection.execute(testSubCollectionAlias).then((collection) => { + expect(collection).toBeDefined() + expect(collection.name).toBe('Scientific Research') + expect(collection.alias).toBe(testSubCollectionAlias) + expect(collection.description).toBe('We do all the science.') + expect(collection.affiliation).toBe('Scientific Research University') + console.log(`Collection ${testSubCollectionAlias} created successfully`, collection) + }) + // TODO: replace this with API call to get the role ids + const roleIds = [1, 2, 3, 4, 5, 6, 7, 8] + const publicationStatuses = [PublicationStatus.Draft, PublicationStatus.Unpublished] + const collectionItemTypes = [ + CollectionItemType.COLLECTION, + CollectionItemType.DATASET, + CollectionItemType.FILE + ] + let actual = await sut.getMyDataCollectionItems( + roleIds, + collectionItemTypes, + publicationStatuses + ) + console.log(actual) + const actualFilePreview = actual.items.find( + (item) => item.type === CollectionItemType.FILE + ) as FilePreview + const actualDatasetPreview = actual.items.find( + (item) => item.type === CollectionItemType.DATASET + ) as DatasetPreview + const actualCollectionPreview = actual.items.find( + (item) => item.type === CollectionItemType.COLLECTION + ) as CollectionPreview + + const expectedFileMd5 = '799b5c8c5fdcfbd56c3943f7a6c35326' + const expectedDatasetCitationFragment = `Admin, Dataverse; Owner, Dataverse, ${currentYear}, "Dataset created using the createDataset use case"` + const expectedDatasetDescription = 'Dataset created using the createDataset use case' + const expectedFileName = 'test-file-2.txt' + const expectedCollectionsName = 'Scientific Research' + + const expectedFacetsAll = [ + { + name: 'publicationStatus', + friendlyName: 'Publication Status', + labels: [ + { name: 'Published', count: 0 }, + { name: 'Unpublished', count: 3 }, + { name: 'Draft', count: 2 }, + { name: 'In Review', count: 0 }, + { name: 'Deaccessioned', count: 0 } + ] + } + ] + expect(actual.items.length).toBe(3) + expect(actual.totalItemCount).toBe(3) + expect(actual.countPerObjectType.collections).toBe(1) + expect(actual.countPerObjectType.datasets).toBe(1) + expect(actual.countPerObjectType.files).toBe(1) + + expect(actualFilePreview.checksum?.type).toBe('MD5') + expect(actualFilePreview.checksum?.value).toBeDefined() + expect(actualFilePreview.datasetCitation).toContain(expectedDatasetCitationFragment) + expect(actualFilePreview.datasetId).toBe(testDatasetIds.numericId) + expect(actualFilePreview.datasetName).toBe(expectedDatasetDescription) + expect(actualFilePreview.datasetPersistentId).toBe(testDatasetIds.persistentId) + expect(actualFilePreview.description).toBe('') + expect(actualFilePreview.fileContentType).toBe('text/plain') + expect(actualFilePreview.fileId).not.toBeUndefined() + expect(actualFilePreview.fileType).toBe('Plain Text') + expect(actualFilePreview.md5).toBe(expectedFileMd5) + expect(actualFilePreview.name).toBe(expectedFileName) + expect(actualFilePreview.publicationStatuses[0]).toBe(PublicationStatus.Unpublished) + expect(actualFilePreview.publicationStatuses[1]).toBe(PublicationStatus.Draft) + expect(actualFilePreview.sizeInBytes).toBe(12) + expect(actualFilePreview.url).not.toBeUndefined() + expect(actualFilePreview.releaseOrCreateDate).not.toBeUndefined() + expect(actualFilePreview.type).toBe(CollectionItemType.FILE) + expect(actualFilePreview.restricted).toBe(false) + expect(actualFilePreview.canDownloadFile).toBe(true) + + expect(actualDatasetPreview.title).toBe(expectedDatasetDescription) + expect(actualDatasetPreview.citation).toContain(expectedDatasetCitationFragment) + expect(actualDatasetPreview.description).toBe('This is the description of the dataset.') + expect(actualDatasetPreview.persistentId).not.toBeUndefined() + expect(actualDatasetPreview.persistentId).not.toBeUndefined() + expect(actualDatasetPreview.publicationStatuses[0]).toBe(PublicationStatus.Unpublished) + expect(actualDatasetPreview.publicationStatuses[1]).toBe(PublicationStatus.Draft) + expect(actualDatasetPreview.versionId).not.toBeUndefined() + expect(actualDatasetPreview.versionInfo.createTime).not.toBeUndefined() + expect(actualDatasetPreview.versionInfo.lastUpdateTime).not.toBeUndefined() + expect(actualDatasetPreview.versionInfo.majorNumber).toBeUndefined() + expect(actualDatasetPreview.versionInfo.minorNumber).toBeUndefined() + expect(actualDatasetPreview.versionInfo.state).toBe('DRAFT') + expect(actualDatasetPreview.parentCollectionAlias).toBe( + 'collectionsRepositoryMyDataCollection' + ) + expect(actualDatasetPreview.parentCollectionName).toBe(expectedCollectionsName) + expect(actualDatasetPreview.type).toBe(CollectionItemType.DATASET) + + expect(actualCollectionPreview.name).toBe(expectedCollectionsName) + expect(actualCollectionPreview.alias).toBe(testSubCollectionAlias) + expect(actualCollectionPreview.description).toBe('We do all the science.') + expect(actualCollectionPreview.imageUrl).toBe(undefined) + expect(actualCollectionPreview.parentAlias).toBe(testCollectionAlias) + expect(actualCollectionPreview.parentName).toBe(expectedCollectionsName) + expect(actualCollectionPreview.publicationStatuses[0]).toBe(PublicationStatus.Unpublished) + expect(actualCollectionPreview.releaseOrCreateDate).not.toBeUndefined() + expect(actualCollectionPreview.affiliation).toBe('Scientific Research University') + expect(actualCollectionPreview.parentAlias).toBe('collectionsRepositoryTestCollection') + expect(actualCollectionPreview.parentName).toBe(expectedCollectionsName) + expect(actualCollectionPreview.type).toBe(CollectionItemType.COLLECTION) + + expect(actual.facets).toEqual(expectedFacetsAll) + + // Test limit and selectedPage + actual = await sut.getMyDataCollectionItems( + roleIds, + collectionItemTypes, + publicationStatuses, + 1, + 1 + ) + expect((actual.items[1] as FilePreview).name).toBe(expectedFileName) + expect(actual.items.length).toBe(3) + expect(actual.totalItemCount).toBe(3) + + // Test search text + const fileNameSearchText = 'test-fi' + + actual = await sut.getMyDataCollectionItems( + roleIds, + collectionItemTypes, + publicationStatuses, + undefined, + undefined, + fileNameSearchText + ) + expect(actual.totalItemCount).toBe(1) + expect((actual.items[0] as FilePreview).name).toBe(expectedFileName) + expect(actual.countPerObjectType.collections).toBe(0) + expect(actual.countPerObjectType.datasets).toBe(0) + expect(actual.countPerObjectType.files).toBe(1) + + const datasetSearchText = 'This is the description' + + actual = await sut.getMyDataCollectionItems( + roleIds, + collectionItemTypes, + publicationStatuses, + undefined, + undefined, + datasetSearchText + ) + expect(actual.totalItemCount).toBe(1) + expect((actual.items[0] as DatasetPreview).title).toBe(expectedDatasetDescription) + expect(actual.countPerObjectType.collections).toBe(0) + expect(actual.countPerObjectType.datasets).toBe(1) + expect(actual.countPerObjectType.files).toBe(0) + + const searchTextForDatasetAndCollection = 'the' + actual = await sut.getMyDataCollectionItems( + roleIds, + collectionItemTypes, + publicationStatuses, + undefined, + undefined, + searchTextForDatasetAndCollection + ) + expect(actual.totalItemCount).toBe(3) + expect((actual.items[0] as DatasetPreview).title).toBe(expectedDatasetDescription) + expect((actual.items[1] as CollectionPreview).name).toBe(expectedCollectionsName) + expect((actual.items[2] as CollectionPreview).name).toBe(expectedCollectionsName) + expect(actual.facets as CollectionItemsFacet[]).toEqual([ + { + name: 'publicationStatus', + friendlyName: 'Publication Status', + labels: [ + { name: 'Published', count: 0 }, + { name: 'Unpublished', count: 2 }, + { name: 'Draft', count: 1 }, + { name: 'In Review', count: 0 }, + { name: 'Deaccessioned', count: 0 } + ] + } + ]) + expect(actual.countPerObjectType.collections).toBe(1) + expect(actual.countPerObjectType.datasets).toBe(1) + expect(actual.countPerObjectType.files).toBe(0) + + // Test search text, limit and offset + // TODO: run this test when the limit param has been fixed in the Dataverse API + /* + actual = await sut.getMyDataCollectionItems( + roleIds, + collectionItemTypes, + publicationStatuses, + 1, + 1, + searchTextForDatasetAndCollection + ) + expect(actual.items.length).toBe(1) + expect(actual.totalItemCount).toBe(2) + expect((actual.items[0] as CollectionPreview).name).toBe(expectedCollectionsName) + expect(actual.countPerObjectType.collections).toBe(1) + expect(actual.countPerObjectType.datasets).toBe(1) + expect(actual.countPerObjectType.files).toBe(0) + */ + + // Test type collection + const searchForCollectionType = [CollectionItemType.COLLECTION] + actual = await sut.getMyDataCollectionItems( + roleIds, + searchForCollectionType, + publicationStatuses, + undefined, + undefined + ) + expect(actual.items.length).toBe(1) + expect(actual.totalItemCount).toBe(1) + expect((actual.items[0] as CollectionPreview).name).toBe(expectedCollectionsName) + expect(actual.facets).toEqual([ + { + name: 'publicationStatus', + friendlyName: 'Publication Status', + labels: [ + { name: 'Published', count: 0 }, + { name: 'Unpublished', count: 1 }, + { name: 'Draft', count: 0 }, + { name: 'In Review', count: 0 }, + { name: 'Deaccessioned', count: 0 } + ] + } + ]) + expect(actual.countPerObjectType.collections).toBe(1) + expect(actual.countPerObjectType.datasets).toBe(0) + expect(actual.countPerObjectType.files).toBe(0) + + // Test type dataset + const searchDatasetType = [CollectionItemType.DATASET] + actual = await sut.getMyDataCollectionItems( + roleIds, + searchDatasetType, + publicationStatuses, + undefined, + undefined + ) + expect(actual.items.length).toBe(1) + expect(actual.totalItemCount).toBe(1) + expect((actual.items[0] as DatasetPreview).title).toBe(expectedDatasetDescription) + expect(actual.facets).toEqual([ + { + name: 'publicationStatus', + friendlyName: 'Publication Status', + labels: [ + { name: 'Published', count: 0 }, + { name: 'Unpublished', count: 1 }, + { name: 'Draft', count: 1 }, + { name: 'In Review', count: 0 }, + { name: 'Deaccessioned', count: 0 } + ] + } + ]) + expect(actual.countPerObjectType.collections).toBe(0) + expect(actual.countPerObjectType.datasets).toBe(1) + expect(actual.countPerObjectType.files).toBe(0) + + // Test type file + const searchFileType = [CollectionItemType.FILE] + + actual = await sut.getMyDataCollectionItems( + roleIds, + searchFileType, + publicationStatuses, + undefined, + undefined + ) + expect(actual.items.length).toBe(1) + expect(actual.totalItemCount).toBe(1) + expect((actual.items[0] as FilePreview).name).toBe(expectedFileName) + expect(actual.facets).toEqual([ + { + name: 'publicationStatus', + friendlyName: 'Publication Status', + labels: [ + { name: 'Published', count: 0 }, + { name: 'Unpublished', count: 1 }, + { name: 'Draft', count: 1 }, + { name: 'In Review', count: 0 }, + { name: 'Deaccessioned', count: 0 } + ] + } + ]) + + expect(actual.countPerObjectType.collections).toBe(0) + expect(actual.countPerObjectType.datasets).toBe(0) + expect(actual.countPerObjectType.files).toBe(1) + + // Test multiple types + const searchForMultiTypes = [CollectionItemType.FILE, CollectionItemType.COLLECTION] + actual = await sut.getMyDataCollectionItems( + roleIds, + searchForMultiTypes, + publicationStatuses, + undefined, + undefined + ) + expect(actual.items.length).toBe(2) + expect(actual.totalItemCount).toBe(2) + expect((actual.items[0] as FilePreview).name).toBe(expectedFileName) + expect((actual.items[1] as CollectionPreview).name).toBe(expectedCollectionsName) + expect(actual.countPerObjectType.collections).toBe(2) + expect(actual.countPerObjectType.datasets).toBe(0) + expect(actual.countPerObjectType.files).toBe(1) + }) + + test('should return error when role, type and publication status params are empty', async () => { + const expectedError = new ReadError('No results. Please select at least one Role.') + + await expect( + sut.getMyDataCollectionItems([], [], [], 0, 0, undefined, undefined) + ).rejects.toThrow(expectedError) + }) + }) }) diff --git a/test/testHelpers/TestConstants.ts b/test/testHelpers/TestConstants.ts index 4a2adfb8..192781b2 100644 --- a/test/testHelpers/TestConstants.ts +++ b/test/testHelpers/TestConstants.ts @@ -2,6 +2,7 @@ import { DatasetDTO } from '../../src/datasets/domain/dtos/DatasetDTO' export class TestConstants { static readonly TEST_API_URL = 'http://localhost:8080/api/v1' + static readonly BUILTIN_USER_KEY = 'builtInS3kretKey' static readonly TEST_DUMMY_API_KEY = 'dummyApiKey' static readonly TEST_DUMMY_PERSISTENT_ID = 'doi:11.1111/AA1/AA1AAA' static readonly TEST_BEARER_TOKEN_LOCAL_STORAGE_KEY = 'DV_TOKEN' diff --git a/test/testHelpers/collections/collectionHelper.ts b/test/testHelpers/collections/collectionHelper.ts index 8ccde481..4548992f 100644 --- a/test/testHelpers/collections/collectionHelper.ts +++ b/test/testHelpers/collections/collectionHelper.ts @@ -90,7 +90,8 @@ export async function createCollectionViaApi( if (parentCollectionAlias == undefined) { parentCollectionAlias = ':root' } - + // Print api key to make sure it is set + console.log(`creating collection, API Key: ${process.env.TEST_API_KEY}`) return await axios .post( `${TestConstants.TEST_API_URL}/dataverses/${parentCollectionAlias}`, diff --git a/test/testHelpers/users/apiTokenHelper.ts b/test/testHelpers/users/apiTokenHelper.ts index 36df6729..a556ade1 100644 --- a/test/testHelpers/users/apiTokenHelper.ts +++ b/test/testHelpers/users/apiTokenHelper.ts @@ -17,11 +17,15 @@ export const createApiTokenViaApi = async (userName: string): Promise => } } ) - return axios + const token = await axios .get(`${TestConstants.TEST_API_URL}/builtin-users/${userName}/api-token?password=${userName}`) .then((response) => response.data.data.message) - } catch (error) { - console.log(error) + await axios.put(`${TestConstants.TEST_API_URL}/admin/superuser/${userName}`, 'true') + console.log(`Created API token for user ${userName}: ${token}`) + return token + } catch (error: Error | any) { + console.log(error.message) + throw new Error(`Error while creating API token`) } } diff --git a/test/testHelpers/users/builtinUserApiHelper.ts b/test/testHelpers/users/builtinUserApiHelper.ts new file mode 100644 index 00000000..d91e08a3 --- /dev/null +++ b/test/testHelpers/users/builtinUserApiHelper.ts @@ -0,0 +1,27 @@ +import axios from 'axios' +import { TestConstants } from '../TestConstants' + +export const createBuiltInUser = async (userName: string): Promise => { + try { + await axios.post( + `${TestConstants.TEST_API_URL}/builtin-users?key=burrito&password=${userName}`, + JSON.stringify({ + userName: userName, + firstName: 'John', + lastName: 'Doe', + email: `${userName}@test.com` + }), + { + headers: { + 'Content-Type': 'application/json' + } + } + ) + return axios + .get(`${TestConstants.TEST_API_URL}/builtin-users/${userName}/api-token?password=${userName}`) + .then((response) => response.data.data.message) + } catch (error) { + console.log(error) + throw new Error(`Error while creating API token`) + } +} From d7a1f4a1894c2a92bfbc84b2c726f7e9d6c416a1 Mon Sep 17 00:00:00 2001 From: Ellen Kraffmiller Date: Fri, 2 May 2025 14:49:07 -0400 Subject: [PATCH 23/28] fix integration test --- .../collections/CollectionsRepository.test.ts | 83 +++++++------------ 1 file changed, 29 insertions(+), 54 deletions(-) diff --git a/test/integration/collections/CollectionsRepository.test.ts b/test/integration/collections/CollectionsRepository.test.ts index ded95492..1155d3b2 100644 --- a/test/integration/collections/CollectionsRepository.test.ts +++ b/test/integration/collections/CollectionsRepository.test.ts @@ -12,7 +12,8 @@ import { ReadError, WriteError, createDataset, - getCollection + getCollection, + createCollection } from '../../../src' import { ApiConfig } from '../../../src' import { DataverseApiAuthMechanism } from '../../../src/core/infra/repositories/ApiConfig' @@ -42,7 +43,6 @@ import { deleteCollectionFeaturedItemsViaApi, deleteCollectionFeaturedItemViaApi } from '../../testHelpers/collections/collectionFeaturedItemsHelper' -import { CollectionItemsFacet } from '../../../src/collections/domain/models/CollectionItemSubset' import { createApiTokenViaApi } from '../../testHelpers/users/apiTokenHelper' describe('CollectionsRepository', () => { @@ -1331,6 +1331,7 @@ describe('CollectionsRepository', () => { const testTextFile1Name = 'test-file-2.txt' const testSubCollectionAlias = 'collectionsRepositoryMyDataCollection' + const testCollectionName = 'Scientific Research' beforeAll(async () => { const myDataUserApiToken = await createApiTokenViaApi('myDataUser') ApiConfig.init( @@ -1339,13 +1340,22 @@ describe('CollectionsRepository', () => { myDataUserApiToken ) process.env.TEST_API_KEY = myDataUserApiToken - + const collectionDTO = createCollectionDTO(testSubCollectionAlias) + await createCollection.execute(collectionDTO, testCollectionAlias).catch(() => { + throw new Error( + `Tests beforeAll(): Error while creating subcollection ${testSubCollectionAlias}` + ) + }) + /* + This does not work, it doesn't return the collection - even though calling the use case does work. await createCollectionViaApi(testSubCollectionAlias).catch((error) => { console.log(error.message) throw new Error( `Tests beforeAll(): Error while creating subcollection ${testSubCollectionAlias}` ) }) + + */ try { testDatasetIds = await createDataset.execute( TestConstants.TEST_NEW_DATASET_DTO, @@ -1376,17 +1386,15 @@ describe('CollectionsRepository', () => { ) } }) - // TODO: remove this skip when the test is fixed - test.skip('should return collection items given valid roleIds', async () => { + test('should return collection items given valid roleIds', async () => { // Give enough time to Solr for indexing await new Promise((resolve) => setTimeout(resolve, 5000)) await getCollection.execute(testSubCollectionAlias).then((collection) => { expect(collection).toBeDefined() - expect(collection.name).toBe('Scientific Research') + expect(collection.name).toBe('Test Collection') expect(collection.alias).toBe(testSubCollectionAlias) - expect(collection.description).toBe('We do all the science.') - expect(collection.affiliation).toBe('Scientific Research University') - console.log(`Collection ${testSubCollectionAlias} created successfully`, collection) + expect(collection.description).toBe('test description') + expect(collection.affiliation).toBe('test affiliation') }) // TODO: replace this with API call to get the role ids const roleIds = [1, 2, 3, 4, 5, 6, 7, 8] @@ -1401,7 +1409,6 @@ describe('CollectionsRepository', () => { collectionItemTypes, publicationStatuses ) - console.log(actual) const actualFilePreview = actual.items.find( (item) => item.type === CollectionItemType.FILE ) as FilePreview @@ -1416,7 +1423,7 @@ describe('CollectionsRepository', () => { const expectedDatasetCitationFragment = `Admin, Dataverse; Owner, Dataverse, ${currentYear}, "Dataset created using the createDataset use case"` const expectedDatasetDescription = 'Dataset created using the createDataset use case' const expectedFileName = 'test-file-2.txt' - const expectedCollectionsName = 'Scientific Research' + const expectedCollectionsName = 'Test Collection' const expectedFacetsAll = [ { @@ -1479,15 +1486,13 @@ describe('CollectionsRepository', () => { expect(actualCollectionPreview.name).toBe(expectedCollectionsName) expect(actualCollectionPreview.alias).toBe(testSubCollectionAlias) - expect(actualCollectionPreview.description).toBe('We do all the science.') + expect(actualCollectionPreview.description).toBe('test description') expect(actualCollectionPreview.imageUrl).toBe(undefined) expect(actualCollectionPreview.parentAlias).toBe(testCollectionAlias) - expect(actualCollectionPreview.parentName).toBe(expectedCollectionsName) + expect(actualCollectionPreview.parentName).toBe(testCollectionName) expect(actualCollectionPreview.publicationStatuses[0]).toBe(PublicationStatus.Unpublished) expect(actualCollectionPreview.releaseOrCreateDate).not.toBeUndefined() - expect(actualCollectionPreview.affiliation).toBe('Scientific Research University') - expect(actualCollectionPreview.parentAlias).toBe('collectionsRepositoryTestCollection') - expect(actualCollectionPreview.parentName).toBe(expectedCollectionsName) + expect(actualCollectionPreview.affiliation).toBe('test affiliation') expect(actualCollectionPreview.type).toBe(CollectionItemType.COLLECTION) expect(actual.facets).toEqual(expectedFacetsAll) @@ -1505,9 +1510,9 @@ describe('CollectionsRepository', () => { expect(actual.totalItemCount).toBe(3) // Test search text - const fileNameSearchText = 'test-fi' + const fileNameSearchText = 'file-2' - actual = await sut.getMyDataCollectionItems( + const actualFileResult = await sut.getMyDataCollectionItems( roleIds, collectionItemTypes, publicationStatuses, @@ -1515,11 +1520,11 @@ describe('CollectionsRepository', () => { undefined, fileNameSearchText ) - expect(actual.totalItemCount).toBe(1) - expect((actual.items[0] as FilePreview).name).toBe(expectedFileName) - expect(actual.countPerObjectType.collections).toBe(0) - expect(actual.countPerObjectType.datasets).toBe(0) - expect(actual.countPerObjectType.files).toBe(1) + expect(actualFileResult.totalItemCount).toBe(1) + expect((actualFileResult.items[0] as FilePreview).name).toBe(expectedFileName) + expect(actualFileResult.countPerObjectType.collections).toBe(0) + expect(actualFileResult.countPerObjectType.datasets).toBe(0) + expect(actualFileResult.countPerObjectType.files).toBe(1) const datasetSearchText = 'This is the description' @@ -1537,36 +1542,6 @@ describe('CollectionsRepository', () => { expect(actual.countPerObjectType.datasets).toBe(1) expect(actual.countPerObjectType.files).toBe(0) - const searchTextForDatasetAndCollection = 'the' - actual = await sut.getMyDataCollectionItems( - roleIds, - collectionItemTypes, - publicationStatuses, - undefined, - undefined, - searchTextForDatasetAndCollection - ) - expect(actual.totalItemCount).toBe(3) - expect((actual.items[0] as DatasetPreview).title).toBe(expectedDatasetDescription) - expect((actual.items[1] as CollectionPreview).name).toBe(expectedCollectionsName) - expect((actual.items[2] as CollectionPreview).name).toBe(expectedCollectionsName) - expect(actual.facets as CollectionItemsFacet[]).toEqual([ - { - name: 'publicationStatus', - friendlyName: 'Publication Status', - labels: [ - { name: 'Published', count: 0 }, - { name: 'Unpublished', count: 2 }, - { name: 'Draft', count: 1 }, - { name: 'In Review', count: 0 }, - { name: 'Deaccessioned', count: 0 } - ] - } - ]) - expect(actual.countPerObjectType.collections).toBe(1) - expect(actual.countPerObjectType.datasets).toBe(1) - expect(actual.countPerObjectType.files).toBe(0) - // Test search text, limit and offset // TODO: run this test when the limit param has been fixed in the Dataverse API /* @@ -1688,7 +1663,7 @@ describe('CollectionsRepository', () => { expect(actual.totalItemCount).toBe(2) expect((actual.items[0] as FilePreview).name).toBe(expectedFileName) expect((actual.items[1] as CollectionPreview).name).toBe(expectedCollectionsName) - expect(actual.countPerObjectType.collections).toBe(2) + expect(actual.countPerObjectType.collections).toBe(1) expect(actual.countPerObjectType.datasets).toBe(0) expect(actual.countPerObjectType.files).toBe(1) }) From d17df326846ea9d55bdda6357683133db854f37e Mon Sep 17 00:00:00 2001 From: Ellen Kraffmiller Date: Fri, 2 May 2025 16:34:15 -0400 Subject: [PATCH 24/28] remove logging, make create superuser conditional --- jest.config.functional.ts | 1 + .../useCases/GetMyDataCollectionItems.ts | 18 +++--------------- .../repositories/CollectionsRepository.ts | 12 ------------ .../collections/CollectionsRepository.test.ts | 6 ++++-- .../collections/collectionHelper.ts | 2 -- test/testHelpers/users/apiTokenHelper.ts | 10 +++++++--- 6 files changed, 15 insertions(+), 34 deletions(-) diff --git a/jest.config.functional.ts b/jest.config.functional.ts index db0c2c93..01a71ab8 100644 --- a/jest.config.functional.ts +++ b/jest.config.functional.ts @@ -2,4 +2,5 @@ import config from './jest.config' config.modulePathIgnorePatterns = ['/test/unit', '/test/integration'] console.log('RUNNING FUNCTIONAL TESTS') + export default config diff --git a/src/collections/domain/useCases/GetMyDataCollectionItems.ts b/src/collections/domain/useCases/GetMyDataCollectionItems.ts index 14541e08..a8260e1f 100644 --- a/src/collections/domain/useCases/GetMyDataCollectionItems.ts +++ b/src/collections/domain/useCases/GetMyDataCollectionItems.ts @@ -18,8 +18,9 @@ export class GetMyDataCollectionItems implements UseCase { * @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} [selected] - Offset (starting point) 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} */ async execute( @@ -31,17 +32,7 @@ export class GetMyDataCollectionItems implements UseCase { searchText?: string, otherUserName?: string ): Promise { - console.log('Execute called with params:', { - roleIds, - collectionItemTypes, - publicationStatuses, - limit, - selectedPage, - searchText, - otherUserName - }) - - const result = await this.collectionsRepository.getMyDataCollectionItems( + return this.collectionsRepository.getMyDataCollectionItems( roleIds, collectionItemTypes, publicationStatuses, @@ -50,8 +41,5 @@ export class GetMyDataCollectionItems implements UseCase { searchText, otherUserName ) - - console.log('Execute returned:', result) - return result } } diff --git a/src/collections/infra/repositories/CollectionsRepository.ts b/src/collections/infra/repositories/CollectionsRepository.ts index 978cba5b..4123d181 100644 --- a/src/collections/infra/repositories/CollectionsRepository.ts +++ b/src/collections/infra/repositories/CollectionsRepository.ts @@ -210,16 +210,6 @@ export class CollectionsRepository extends ApiRepository implements ICollections searchText?: string, userIdentifier?: string ): Promise { - console.log('getMyDataCollectionItems called with params:', { - roleIds, - collectionItemTypes, - publicationStatuses, - limit, - selectedPage, - searchText, - userIdentifier - }) - const queryParams = new URLSearchParams() if (limit) { @@ -267,7 +257,6 @@ export class CollectionsRepository extends ApiRepository implements ICollections publicationStatus.toString() ) }) - console.log('getMyDataCollectionItems queryParams:', queryParams) const result = await this.doGet('/mydata/retrieve', true, queryParams) .then((response) => { if (response.data.success !== true) { @@ -279,7 +268,6 @@ export class CollectionsRepository extends ApiRepository implements ICollections throw error }) - console.log('getMyDataCollectionItems returned:', result) return result } diff --git a/test/integration/collections/CollectionsRepository.test.ts b/test/integration/collections/CollectionsRepository.test.ts index 1155d3b2..b40dfc2c 100644 --- a/test/integration/collections/CollectionsRepository.test.ts +++ b/test/integration/collections/CollectionsRepository.test.ts @@ -1333,7 +1333,8 @@ describe('CollectionsRepository', () => { const testSubCollectionAlias = 'collectionsRepositoryMyDataCollection' const testCollectionName = 'Scientific Research' beforeAll(async () => { - const myDataUserApiToken = await createApiTokenViaApi('myDataUser') + const createSuperUser = true + const myDataUserApiToken = await createApiTokenViaApi('myDataUser', createSuperUser) ApiConfig.init( TestConstants.TEST_API_URL, DataverseApiAuthMechanism.API_KEY, @@ -1341,7 +1342,8 @@ describe('CollectionsRepository', () => { ) process.env.TEST_API_KEY = myDataUserApiToken const collectionDTO = createCollectionDTO(testSubCollectionAlias) - await createCollection.execute(collectionDTO, testCollectionAlias).catch(() => { + await createCollection.execute(collectionDTO, testCollectionAlias).catch((error) => { + console.log(error.message) throw new Error( `Tests beforeAll(): Error while creating subcollection ${testSubCollectionAlias}` ) diff --git a/test/testHelpers/collections/collectionHelper.ts b/test/testHelpers/collections/collectionHelper.ts index 4548992f..b19b668f 100644 --- a/test/testHelpers/collections/collectionHelper.ts +++ b/test/testHelpers/collections/collectionHelper.ts @@ -90,8 +90,6 @@ export async function createCollectionViaApi( if (parentCollectionAlias == undefined) { parentCollectionAlias = ':root' } - // Print api key to make sure it is set - console.log(`creating collection, API Key: ${process.env.TEST_API_KEY}`) return await axios .post( `${TestConstants.TEST_API_URL}/dataverses/${parentCollectionAlias}`, diff --git a/test/testHelpers/users/apiTokenHelper.ts b/test/testHelpers/users/apiTokenHelper.ts index a556ade1..49b25cd3 100644 --- a/test/testHelpers/users/apiTokenHelper.ts +++ b/test/testHelpers/users/apiTokenHelper.ts @@ -1,7 +1,10 @@ import axios from 'axios' import { TestConstants } from '../TestConstants' -export const createApiTokenViaApi = async (userName: string): Promise => { +export const createApiTokenViaApi = async ( + userName: string, + createSuperUser = false +): Promise => { try { await axios.post( `${TestConstants.TEST_API_URL}/builtin-users?key=burrito&password=${userName}`, @@ -20,8 +23,9 @@ export const createApiTokenViaApi = async (userName: string): Promise => const token = await axios .get(`${TestConstants.TEST_API_URL}/builtin-users/${userName}/api-token?password=${userName}`) .then((response) => response.data.data.message) - await axios.put(`${TestConstants.TEST_API_URL}/admin/superuser/${userName}`, 'true') - console.log(`Created API token for user ${userName}: ${token}`) + if (createSuperUser) { + await axios.put(`${TestConstants.TEST_API_URL}/admin/superuser/${userName}`, 'true') + } return token } catch (error: Error | any) { console.log(error.message) From af1dd723437a30e8ba54009e2a18c2a361536f7c Mon Sep 17 00:00:00 2001 From: Ellen Kraffmiller Date: Fri, 2 May 2025 17:06:08 -0400 Subject: [PATCH 25/28] remove commented code --- .../collections/CollectionsRepository.test.ts | 13 +++---------- 1 file changed, 3 insertions(+), 10 deletions(-) diff --git a/test/integration/collections/CollectionsRepository.test.ts b/test/integration/collections/CollectionsRepository.test.ts index b40dfc2c..83b026e2 100644 --- a/test/integration/collections/CollectionsRepository.test.ts +++ b/test/integration/collections/CollectionsRepository.test.ts @@ -1342,22 +1342,15 @@ describe('CollectionsRepository', () => { ) process.env.TEST_API_KEY = myDataUserApiToken const collectionDTO = createCollectionDTO(testSubCollectionAlias) + // Calling the createCollection use case here + // because createCollectionViaApi does not create the collection in a way + // that is recognized by then MyData endpoint await createCollection.execute(collectionDTO, testCollectionAlias).catch((error) => { console.log(error.message) throw new Error( `Tests beforeAll(): Error while creating subcollection ${testSubCollectionAlias}` ) }) - /* - This does not work, it doesn't return the collection - even though calling the use case does work. - await createCollectionViaApi(testSubCollectionAlias).catch((error) => { - console.log(error.message) - throw new Error( - `Tests beforeAll(): Error while creating subcollection ${testSubCollectionAlias}` - ) - }) - - */ try { testDatasetIds = await createDataset.execute( TestConstants.TEST_NEW_DATASET_DTO, From 47d8743d3909f5338815789dd1bd8c419cce9f49 Mon Sep 17 00:00:00 2001 From: Ellen Kraffmiller Date: Fri, 2 May 2025 17:10:55 -0400 Subject: [PATCH 26/28] fix use case description --- docs/useCases.md | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/docs/useCases.md b/docs/useCases.md index 2250ee31..2b2af4a9 100644 --- a/docs/useCases.md +++ b/docs/useCases.md @@ -265,15 +265,12 @@ This use case requires the following parameters: 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 `MyDataCollectionItemSubset`returned instance contains a property called `totalItemCount` which is necessary for pagination. - -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) +The `CollectionItemSubset`returned instance contains a property called `totalItemCount` which is necessary for pagination. #### Get Collection Featured Items From 728df660046f2981454a1276fde4df767c9bcebe Mon Sep 17 00:00:00 2001 From: Ellen Kraffmiller Date: Fri, 2 May 2025 17:13:49 -0400 Subject: [PATCH 27/28] fix return type in documentation --- src/collections/domain/useCases/GetMyDataCollectionItems.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/collections/domain/useCases/GetMyDataCollectionItems.ts b/src/collections/domain/useCases/GetMyDataCollectionItems.ts index a8260e1f..5af00187 100644 --- a/src/collections/domain/useCases/GetMyDataCollectionItems.ts +++ b/src/collections/domain/useCases/GetMyDataCollectionItems.ts @@ -21,7 +21,7 @@ export class GetMyDataCollectionItems implements UseCase { * @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} + * * @returns {Promise} */ async execute( roleIds: number[], From 51208b76052184b17b664202d0db9a3c30e5e7e1 Mon Sep 17 00:00:00 2001 From: Ellen Kraffmiller Date: Mon, 5 May 2025 10:23:08 -0400 Subject: [PATCH 28/28] remove console.log() --- test/testHelpers/users/apiTokenHelper.ts | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/test/testHelpers/users/apiTokenHelper.ts b/test/testHelpers/users/apiTokenHelper.ts index 49b25cd3..b99608b1 100644 --- a/test/testHelpers/users/apiTokenHelper.ts +++ b/test/testHelpers/users/apiTokenHelper.ts @@ -27,9 +27,7 @@ export const createApiTokenViaApi = async ( await axios.put(`${TestConstants.TEST_API_URL}/admin/superuser/${userName}`, 'true') } return token - } catch (error: Error | any) { - console.log(error.message) - + } catch (error) { throw new Error(`Error while creating API token`) } }