diff --git a/docs/useCases.md b/docs/useCases.md index edc4f407..ae3aaae0 100644 --- a/docs/useCases.md +++ b/docs/useCases.md @@ -23,6 +23,7 @@ The different use cases currently available in the package are classified below, - [Delete a Collection](#delete-a-collection) - [Update Collection Featured Items](#update-collection-featured-items) - [Delete Collection Featured Items](#delete-collection-featured-items) + - [Delete a Collection Featured Item](#delete-a-collection-featured-item) - [Datasets](#Datasets) - [Datasets read use cases](#datasets-read-use-cases) - [Get a Dataset](#get-a-dataset) @@ -277,7 +278,7 @@ The `CollectionItemSubset`returned instance contains a property called `totalIte #### 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. +Returns a [FeaturedItem](../src/collections/domain/models/CollectionFeaturedItem.ts) array containing the featured items of the requested collection, given the collection identifier or alias. ##### Example call: @@ -288,7 +289,7 @@ const collectionIdOrAlias = 12345 getCollectionFeaturedItems .execute(collectionId) - .then((featuredItems: CollectionFeaturedItem[]) => { + .then((featuredItems: FeaturedItem[]) => { /* ... */ }) .catch((error: Error) => { @@ -405,7 +406,7 @@ _See [use case](../src/collections/domain/useCases/DeleteCollection.ts)_ definit #### Update Collection Featured Items -Updates all featured items, given a collection identifier and a CollectionFeaturedItemsDTO. +Updates all featured items, given a collection identifier and a FeaturedItemsDTO. ##### Example call: @@ -416,11 +417,9 @@ import { updateCollectionFeaturedItems } from '@iqss/dataverse-client-javascript const collectionIdOrAlias = 12345 -updateCollectionFeaturedItems - .execute(collectionIdOrAlias) - .then((collectionFeaturedItems: CollectionFeaturedItem[]) => { - /* ... */ - }) +updateCollectionFeaturedItems.execute(collectionIdOrAlias).then((featuredItems: FeaturedItem[]) => { + /* ... */ +}) /* ... */ ``` @@ -451,6 +450,26 @@ The `collectionIdOrAlias` is a generic collection identifier, which can be eithe _See [use case](../src/collections/domain/useCases/DeleteCollectionFeaturedItems.ts)_ definition. +#### Delete A Collection Featured Item + +Deletes a single featured item, given a featured item id. + +##### Example call: + +```typescript +import { deleteCollectionFeaturedItem } from '@iqss/dataverse-client-javascript' + +/* ... */ + +const featuredItemId = 12345 + +deleteCollectionFeaturedItem.execute(featuredItemId) + +/* ... */ +``` + +_See [use case](../src/collections/domain/useCases/DeleteCollectionFeaturedItem.ts)_ definition. + ## Datasets ### Datasets Read Use Cases diff --git a/src/collections/domain/dtos/CollectionFeaturedItemsDTO.ts b/src/collections/domain/dtos/CollectionFeaturedItemsDTO.ts deleted file mode 100644 index 7a60052d..00000000 --- a/src/collections/domain/dtos/CollectionFeaturedItemsDTO.ts +++ /dev/null @@ -1,9 +0,0 @@ -export type CollectionFeaturedItemsDTO = CollectionFeaturedItemDTO[] - -export interface CollectionFeaturedItemDTO { - id?: number - content: string - displayOrder: number - file?: File - keepFile: boolean -} diff --git a/src/collections/domain/dtos/FeaturedItemsDTO.ts b/src/collections/domain/dtos/FeaturedItemsDTO.ts new file mode 100644 index 00000000..434e941a --- /dev/null +++ b/src/collections/domain/dtos/FeaturedItemsDTO.ts @@ -0,0 +1,19 @@ +import { CustomFeaturedItem, DvObjectFeaturedItem } from '../models/FeaturedItem' + +export type FeaturedItemsDTO = (CustomFeaturedItemDTO | DvObjectFeaturedItemDTO)[] + +export interface CustomFeaturedItemDTO { + id?: number + type: CustomFeaturedItem['type'] + content: string + displayOrder: number + file?: File + keepFile: boolean +} + +export interface DvObjectFeaturedItemDTO { + id?: number + type: DvObjectFeaturedItem['type'] + dvObjectIdentifier: string + displayOrder: number +} diff --git a/src/collections/domain/models/CollectionFeaturedItem.ts b/src/collections/domain/models/CollectionFeaturedItem.ts deleted file mode 100644 index 88a3777d..00000000 --- a/src/collections/domain/models/CollectionFeaturedItem.ts +++ /dev/null @@ -1,7 +0,0 @@ -export interface CollectionFeaturedItem { - id: number - content: string - imageFileName?: string - imageFileUrl?: string - displayOrder: number -} diff --git a/src/collections/domain/models/FeaturedItem.ts b/src/collections/domain/models/FeaturedItem.ts new file mode 100644 index 00000000..99848c72 --- /dev/null +++ b/src/collections/domain/models/FeaturedItem.ts @@ -0,0 +1,25 @@ +export type FeaturedItem = CustomFeaturedItem | DvObjectFeaturedItem + +export interface CustomFeaturedItem { + id: number + type: FeaturedItemType.CUSTOM + content: string + imageFileName?: string + imageFileUrl?: string + displayOrder: number +} + +export interface DvObjectFeaturedItem { + id: number + type: FeaturedItemType.COLLECTION | FeaturedItemType.DATASET | FeaturedItemType.FILE + dvObjectIdentifier: string + dvObjectDisplayName: string + displayOrder: number +} + +export enum FeaturedItemType { + CUSTOM = 'custom', + COLLECTION = 'collection', + DATASET = 'dataset', + FILE = 'file' +} diff --git a/src/collections/domain/repositories/ICollectionsRepository.ts b/src/collections/domain/repositories/ICollectionsRepository.ts index 6c0baf35..6c78923e 100644 --- a/src/collections/domain/repositories/ICollectionsRepository.ts +++ b/src/collections/domain/repositories/ICollectionsRepository.ts @@ -1,8 +1,8 @@ import { CollectionDTO } from '../dtos/CollectionDTO' -import { CollectionFeaturedItemsDTO } from '../dtos/CollectionFeaturedItemsDTO' +import { FeaturedItemsDTO } from '../dtos/FeaturedItemsDTO' import { Collection } from '../models/Collection' import { CollectionFacet } from '../models/CollectionFacet' -import { CollectionFeaturedItem } from '../models/CollectionFeaturedItem' +import { FeaturedItem } from '../models/FeaturedItem' import { CollectionItemSubset } from '../models/CollectionItemSubset' import { MyDataCollectionItemSubset } from '../models/MyDataCollectionItemSubset' import { CollectionSearchCriteria } from '../models/CollectionSearchCriteria' @@ -42,12 +42,11 @@ export interface ICollectionsRepository { collectionIdOrAlias: number | string, updatedCollection: CollectionDTO ): Promise - getCollectionFeaturedItems( - collectionIdOrAlias: number | string - ): Promise + getCollectionFeaturedItems(collectionIdOrAlias: number | string): Promise updateCollectionFeaturedItems( collectionIdOrAlias: number | string, - featuredItemDTOs: CollectionFeaturedItemsDTO - ): Promise + featuredItemDTOs: FeaturedItemsDTO + ): Promise deleteCollectionFeaturedItems(collectionIdOrAlias: number | string): Promise + deleteCollectionFeaturedItem(featuredItemId: number): Promise } diff --git a/src/collections/domain/useCases/DeleteCollectionFeaturedItem.ts b/src/collections/domain/useCases/DeleteCollectionFeaturedItem.ts new file mode 100644 index 00000000..f8fc7e15 --- /dev/null +++ b/src/collections/domain/useCases/DeleteCollectionFeaturedItem.ts @@ -0,0 +1,21 @@ +import { UseCase } from '../../../core/domain/useCases/UseCase' +import { ICollectionsRepository } from '../repositories/ICollectionsRepository' + +export class DeleteCollectionFeaturedItem implements UseCase { + private collectionsRepository: ICollectionsRepository + + constructor(collectionsRepository: ICollectionsRepository) { + this.collectionsRepository = collectionsRepository + } + + /** + * Deletes a single featured item, given a featured item id. + * + * @param {number} [featuredItemId] - The id of the featured item to delete. + * @returns {Promise} - This method does not return anything upon successful completion. + * @throws {WriteError} - If there are errors while writing data. + */ + async execute(featuredItemId: number): Promise { + return await this.collectionsRepository.deleteCollectionFeaturedItem(featuredItemId) + } +} diff --git a/src/collections/domain/useCases/GetCollectionFeaturedItems.ts b/src/collections/domain/useCases/GetCollectionFeaturedItems.ts index 7a2a0adc..03d43905 100644 --- a/src/collections/domain/useCases/GetCollectionFeaturedItems.ts +++ b/src/collections/domain/useCases/GetCollectionFeaturedItems.ts @@ -1,9 +1,9 @@ import { UseCase } from '../../../core/domain/useCases/UseCase' import { ICollectionsRepository } from '../repositories/ICollectionsRepository' import { ROOT_COLLECTION_ID } from '../models/Collection' -import { CollectionFeaturedItem } from '../models/CollectionFeaturedItem' +import { FeaturedItem } from '../models/FeaturedItem' -export class GetCollectionFeaturedItems implements UseCase { +export class GetCollectionFeaturedItems implements UseCase { private collectionsRepository: ICollectionsRepository constructor(collectionsRepository: ICollectionsRepository) { @@ -11,15 +11,15 @@ export class GetCollectionFeaturedItems implements UseCase} + * @returns {Promise} */ async execute( collectionIdOrAlias: number | string = ROOT_COLLECTION_ID - ): Promise { + ): Promise { return await this.collectionsRepository.getCollectionFeaturedItems(collectionIdOrAlias) } } diff --git a/src/collections/domain/useCases/UpdateCollectionFeaturedItems.ts b/src/collections/domain/useCases/UpdateCollectionFeaturedItems.ts index 10d22b5a..08a1a07f 100644 --- a/src/collections/domain/useCases/UpdateCollectionFeaturedItems.ts +++ b/src/collections/domain/useCases/UpdateCollectionFeaturedItems.ts @@ -1,10 +1,10 @@ import { UseCase } from '../../../core/domain/useCases/UseCase' -import { CollectionFeaturedItemsDTO } from '../dtos/CollectionFeaturedItemsDTO' +import { FeaturedItemsDTO } from '../dtos/FeaturedItemsDTO' import { ROOT_COLLECTION_ID } from '../models/Collection' -import { CollectionFeaturedItem } from '../models/CollectionFeaturedItem' +import { FeaturedItem } from '../models/FeaturedItem' import { ICollectionsRepository } from '../repositories/ICollectionsRepository' -export class UpdateCollectionFeaturedItems implements UseCase { +export class UpdateCollectionFeaturedItems implements UseCase { private collectionsRepository: ICollectionsRepository constructor(collectionsRepository: ICollectionsRepository) { @@ -12,18 +12,18 @@ export class UpdateCollectionFeaturedItems implements UseCase} -This method returns the updated collection featured items upon successful completion. + * @param {FeaturedItemsDTO} [featuredItemsDTO] - FeaturedItemsDTO object including the updated collection featured items data. + * @returns {Promise} -This method returns the updated collection featured items upon successful completion. * @throws {WriteError} - If there are errors while writing data. */ async execute( collectionIdOrAlias: number | string = ROOT_COLLECTION_ID, - featuredItemsDTO: CollectionFeaturedItemsDTO - ): Promise { + featuredItemsDTO: FeaturedItemsDTO + ): Promise { return await this.collectionsRepository.updateCollectionFeaturedItems( collectionIdOrAlias, featuredItemsDTO diff --git a/src/collections/index.ts b/src/collections/index.ts index 07e79ad3..c275402e 100644 --- a/src/collections/index.ts +++ b/src/collections/index.ts @@ -11,6 +11,7 @@ import { UpdateCollectionFeaturedItems } from './domain/useCases/UpdateCollectio import { DeleteCollectionFeaturedItems } from './domain/useCases/DeleteCollectionFeaturedItems' import { DeleteCollection } from './domain/useCases/DeleteCollection' import { GetMyDataCollectionItems } from './domain/useCases/GetMyDataCollectionItems' +import { DeleteCollectionFeaturedItem } from './domain/useCases/DeleteCollectionFeaturedItem' const collectionsRepository = new CollectionsRepository() @@ -26,6 +27,7 @@ const getCollectionFeaturedItems = new GetCollectionFeaturedItems(collectionsRep const updateCollectionFeaturedItems = new UpdateCollectionFeaturedItems(collectionsRepository) const deleteCollectionFeaturedItems = new DeleteCollectionFeaturedItems(collectionsRepository) const deleteCollection = new DeleteCollection(collectionsRepository) +const deleteCollectionFeaturedItem = new DeleteCollectionFeaturedItem(collectionsRepository) export { getCollection, @@ -39,7 +41,8 @@ export { getCollectionFeaturedItems, updateCollectionFeaturedItems, deleteCollectionFeaturedItems, - deleteCollection + deleteCollection, + deleteCollectionFeaturedItem } export { Collection, CollectionInputLevel } from './domain/models/Collection' export { CollectionFacet } from './domain/models/CollectionFacet' @@ -48,5 +51,5 @@ export { CollectionDTO, CollectionInputLevelDTO } from './domain/dtos/Collection export { CollectionPreview } from './domain/models/CollectionPreview' export { CollectionItemType } from './domain/models/CollectionItemType' export { CollectionSearchCriteria } from './domain/models/CollectionSearchCriteria' -export { CollectionFeaturedItem } from './domain/models/CollectionFeaturedItem' -export { CollectionFeaturedItemsDTO } from './domain/dtos/CollectionFeaturedItemsDTO' +export { FeaturedItem } from './domain/models/FeaturedItem' +export { FeaturedItemsDTO } from './domain/dtos/FeaturedItemsDTO' diff --git a/src/collections/infra/repositories/CollectionsRepository.ts b/src/collections/infra/repositories/CollectionsRepository.ts index eb3f2e81..fa0f9872 100644 --- a/src/collections/infra/repositories/CollectionsRepository.ts +++ b/src/collections/infra/repositories/CollectionsRepository.ts @@ -19,9 +19,20 @@ import { SortType } from '../../domain/models/CollectionSearchCriteria' import { CollectionItemType } from '../../domain/models/CollectionItemType' -import { CollectionFeaturedItem } from '../../domain/models/CollectionFeaturedItem' -import { transformCollectionFeaturedItemsPayloadToCollectionFeaturedItems } from './transformers/collectionFeaturedItemsTransformer' -import { CollectionFeaturedItemsDTO } from '../../domain/dtos/CollectionFeaturedItemsDTO' +import { + FeaturedItem, + DvObjectFeaturedItem, + FeaturedItemType +} from '../../domain/models/FeaturedItem' +import { + domainTypeToApiType, + transformFeaturedItemsPayloadToFeaturedItems +} from './transformers/featuredItemsTransformer' +import { + FeaturedItemsDTO, + CustomFeaturedItemDTO, + DvObjectFeaturedItemDTO +} from '../../domain/dtos/FeaturedItemsDTO' import { ApiConstants } from '../../../core/infra/repositories/ApiConstants' import { PublicationStatus } from '../../../core/domain/models/PublicationStatus' import { ReadError } from '../../../core/domain/repositories/ReadError' @@ -351,11 +362,9 @@ export class CollectionsRepository extends ApiRepository implements ICollections public async getCollectionFeaturedItems( collectionIdOrAlias: number | string - ): Promise { + ): Promise { return this.doGet(`/${this.collectionsResourceName}/${collectionIdOrAlias}/featuredItems`, true) - .then((response) => - transformCollectionFeaturedItemsPayloadToCollectionFeaturedItems(response.data.data) - ) + .then((response) => transformFeaturedItemsPayloadToFeaturedItems(response.data.data)) .catch((error) => { throw error }) @@ -363,8 +372,8 @@ export class CollectionsRepository extends ApiRepository implements ICollections public async updateCollectionFeaturedItems( collectionIdOrAlias: number | string, - featuredItemsDTO: CollectionFeaturedItemsDTO - ): Promise { + featuredItemsDTO: FeaturedItemsDTO + ): Promise { const featuredItemsFormData = this.toFeaturedItemsFormData(featuredItemsDTO) return this.doPut( @@ -373,31 +382,43 @@ export class CollectionsRepository extends ApiRepository implements ICollections undefined, ApiConstants.CONTENT_TYPE_MULTIPART_FORM_DATA ) - .then((response) => - transformCollectionFeaturedItemsPayloadToCollectionFeaturedItems(response.data.data) - ) + .then((response) => transformFeaturedItemsPayloadToFeaturedItems(response.data.data)) .catch((error) => { throw error }) } - private toFeaturedItemsFormData(featuredItemsDTO: CollectionFeaturedItemsDTO): FormData { + private toFeaturedItemsFormData(featuredItemsDTO: FeaturedItemsDTO): FormData { // This is not really necessary because we are sending displayOrder property anyways, but I wanted to keep the order of the items in the form data const orderedFeaturedItemsDTO = featuredItemsDTO.sort((a, b) => a.displayOrder - b.displayOrder) const formData = new FormData() - orderedFeaturedItemsDTO.forEach((item) => { - const { id, content, displayOrder, file, keepFile } = item - const fileName = file ? file.name : '' - - formData.append('id', id ? id.toString() : '0') - formData.append('content', content) - formData.append('displayOrder', displayOrder.toString()) - formData.append('keepFile', keepFile.toString()) - formData.append('fileName', fileName) - if (file) { - formData.append('file', file) + orderedFeaturedItemsDTO.forEach((item: CustomFeaturedItemDTO | DvObjectFeaturedItemDTO) => { + formData.append('id', item.id !== undefined ? item.id.toString() : '0') + formData.append('displayOrder', item.displayOrder.toString()) + + if (item.type === FeaturedItemType.CUSTOM) { + // CustomFeaturedItemDTO + formData.append('type', item.type) + formData.append('content', item.content) + formData.append('keepFile', item.keepFile.toString()) + formData.append('fileName', item.file ? item.file.name : '') + if (item.file) { + formData.append('file', item.file) + } + + // We still need to append dvObjectIdentifier as it is expected by the backend even empty + formData.append('dvObjectIdentifier', '') + } else { + // DvObjectFeaturedItemDTO + formData.append('type', domainTypeToApiType[item.type as DvObjectFeaturedItem['type']]) + formData.append('dvObjectIdentifier', item.dvObjectIdentifier) + + // We still need to append content, keepFile, and fileName as they are expected by the backend even empty + formData.append('content', '') + formData.append('keepFile', '') + formData.append('fileName', '') } }) @@ -411,4 +432,12 @@ export class CollectionsRepository extends ApiRepository implements ICollections throw error }) } + + public async deleteCollectionFeaturedItem(featuredItemId: number): Promise { + return this.doDelete(`/dataverseFeaturedItems/${featuredItemId}`) + .then(() => undefined) + .catch((error) => { + throw error + }) + } } diff --git a/src/collections/infra/repositories/transformers/CollectionFeaturedItemPayload.ts b/src/collections/infra/repositories/transformers/CollectionFeaturedItemPayload.ts index 34ba0dcf..06025ee4 100644 --- a/src/collections/infra/repositories/transformers/CollectionFeaturedItemPayload.ts +++ b/src/collections/infra/repositories/transformers/CollectionFeaturedItemPayload.ts @@ -1,7 +1,18 @@ -export interface CollectionFeaturedItemPayload { +export type CollectionFeaturedItemPayload = CustomFeaturedItemPayload | DvObjectFeaturedItemPayload + +export interface CustomFeaturedItemPayload { id: number + type: 'custom' content: string imageFileName: string | null imageFileUrl: string | null displayOrder: number } + +export interface DvObjectFeaturedItemPayload { + id: number + type: 'dataverse' | 'dataset' | 'datafile' + dvObjectIdentifier: string + dvObjectDisplayName: string + displayOrder: number +} diff --git a/src/collections/infra/repositories/transformers/collectionFeaturedItemsTransformer.ts b/src/collections/infra/repositories/transformers/collectionFeaturedItemsTransformer.ts deleted file mode 100644 index f3bfb135..00000000 --- a/src/collections/infra/repositories/transformers/collectionFeaturedItemsTransformer.ts +++ /dev/null @@ -1,16 +0,0 @@ -import { CollectionFeaturedItem } from '../../../domain/models/CollectionFeaturedItem' -import { CollectionFeaturedItemPayload } from './CollectionFeaturedItemPayload' - -export const transformCollectionFeaturedItemsPayloadToCollectionFeaturedItems = ( - collectionFeaturedItemsPayload: CollectionFeaturedItemPayload[] -): CollectionFeaturedItem[] => { - return collectionFeaturedItemsPayload - .map((collectionFeaturedItemPayload) => ({ - id: collectionFeaturedItemPayload.id, - content: collectionFeaturedItemPayload.content, - imageFileUrl: collectionFeaturedItemPayload.imageFileUrl || undefined, - imageFileName: collectionFeaturedItemPayload.imageFileName || undefined, - displayOrder: collectionFeaturedItemPayload.displayOrder - })) - .sort((a, b) => a.displayOrder - b.displayOrder) -} diff --git a/src/collections/infra/repositories/transformers/featuredItemsTransformer.ts b/src/collections/infra/repositories/transformers/featuredItemsTransformer.ts new file mode 100644 index 00000000..e295e7ca --- /dev/null +++ b/src/collections/infra/repositories/transformers/featuredItemsTransformer.ts @@ -0,0 +1,66 @@ +import { + FeaturedItem, + CustomFeaturedItem, + DvObjectFeaturedItem, + FeaturedItemType +} from '../../../domain/models/FeaturedItem' +import { + CollectionFeaturedItemPayload, + DvObjectFeaturedItemPayload +} from './CollectionFeaturedItemPayload' + +const apiTypeToDomainType: Record< + DvObjectFeaturedItemPayload['type'], + DvObjectFeaturedItem['type'] +> = { + dataverse: FeaturedItemType.COLLECTION, + dataset: FeaturedItemType.DATASET, + datafile: FeaturedItemType.FILE +} + +export const domainTypeToApiType: Record< + DvObjectFeaturedItem['type'], + DvObjectFeaturedItemPayload['type'] +> = { + [FeaturedItemType.COLLECTION]: 'dataverse', + [FeaturedItemType.DATASET]: 'dataset', + [FeaturedItemType.FILE]: 'datafile' +} + +export const transformFeaturedItemsPayloadToFeaturedItems = ( + payload: CollectionFeaturedItemPayload[] +): FeaturedItem[] => { + return payload + .map((item) => { + if (item.type === 'custom') { + const customFeaturedItem: CustomFeaturedItem = { + id: item.id, + type: FeaturedItemType.CUSTOM, + content: item.content, + imageFileUrl: item.imageFileUrl || undefined, + imageFileName: item.imageFileName || undefined, + displayOrder: item.displayOrder + } + + return customFeaturedItem + } else { + // Map API types to domain types + const type = apiTypeToDomainType[item.type] + + if (!type) { + throw new Error(`Unknown type: ${item.type}`) + } + + const dvObjectFeaturedItem: DvObjectFeaturedItem = { + id: item.id, + type, + dvObjectIdentifier: item.dvObjectIdentifier, + dvObjectDisplayName: item.dvObjectDisplayName, + displayOrder: item.displayOrder + } + + return dvObjectFeaturedItem + } + }) + .sort((a, b) => a.displayOrder - b.displayOrder) +} diff --git a/test/environment/.env b/test/environment/.env index 0c691d9b..e7b54bde 100644 --- a/test/environment/.env +++ b/test/environment/.env @@ -1,4 +1,4 @@ -POSTGRES_VERSION=13 +POSTGRES_VERSION=17 DATAVERSE_DB_USER=dataverse SOLR_VERSION=9.8.0 DATAVERSE_IMAGE_REGISTRY=docker.io diff --git a/test/functional/collections/DeleteCollectionFeaturedItem.test.ts b/test/functional/collections/DeleteCollectionFeaturedItem.test.ts new file mode 100644 index 00000000..11066f82 --- /dev/null +++ b/test/functional/collections/DeleteCollectionFeaturedItem.test.ts @@ -0,0 +1,70 @@ +import { ApiConfig, deleteCollectionFeaturedItem, WriteError } from '../../../src' +import { TestConstants } from '../../testHelpers/TestConstants' +import { DataverseApiAuthMechanism } from '../../../src/core/infra/repositories/ApiConfig' +import { + createCollectionViaApi, + deleteCollectionViaApi +} from '../../testHelpers/collections/collectionHelper' +import { createCollectionCustomFeaturedItemViaApi } from '../../testHelpers/collections/collectionFeaturedItemsHelper' + +describe('execute', () => { + const testCollectionAlias = 'deleteCollectionFeaturedItemTest' + let featuredItemTestId: number + + beforeEach(async () => { + ApiConfig.init( + TestConstants.TEST_API_URL, + DataverseApiAuthMechanism.API_KEY, + process.env.TEST_API_KEY + ) + }) + + beforeAll(async () => { + try { + await createCollectionViaApi(testCollectionAlias) + const featuredItem = await createCollectionCustomFeaturedItemViaApi(testCollectionAlias, { + content: '

Test content

', + displayOrder: 1, + withFile: true, + fileName: 'featured-item-test-image.png' + }) + featuredItemTestId = featuredItem.id + } catch (error) { + throw new Error( + `Tests beforeAll(): Error while creating test collection: ${testCollectionAlias}` + ) + } + }) + + afterAll(async () => { + try { + await deleteCollectionViaApi(testCollectionAlias) + } catch (error) { + throw new Error( + `Tests afterAll(): Error while deleting test collection: ${testCollectionAlias}` + ) + } + }) + + test('should succesfully delete the featured item', async () => { + const actual = await deleteCollectionFeaturedItem.execute(featuredItemTestId) + + expect(actual).toBeUndefined() + }) + + test('should throw an error when featured item does not exist', async () => { + const invalidFeaturedItemId = 99 + let writeError: WriteError | undefined + + try { + await deleteCollectionFeaturedItem.execute(invalidFeaturedItemId) + } catch (error) { + writeError = error as WriteError + } finally { + expect(writeError).toBeInstanceOf(WriteError) + expect((writeError as WriteError).message).toEqual( + `There was an error when writing the resource. Reason was: [404] Could not find dataverse featured item with identifier ${invalidFeaturedItemId}` + ) + } + }) +}) diff --git a/test/functional/collections/DeleteCollectionFeaturedItems.test.ts b/test/functional/collections/DeleteCollectionFeaturedItems.test.ts index 3f66a0e8..29beb802 100644 --- a/test/functional/collections/DeleteCollectionFeaturedItems.test.ts +++ b/test/functional/collections/DeleteCollectionFeaturedItems.test.ts @@ -6,7 +6,7 @@ import { deleteCollectionViaApi } from '../../testHelpers/collections/collectionHelper' import { - createCollectionFeaturedItemViaApi, + createCollectionCustomFeaturedItemViaApi, deleteCollectionFeaturedItemsViaApi } from '../../testHelpers/collections/collectionFeaturedItemsHelper' @@ -24,18 +24,18 @@ describe('execute', () => { beforeAll(async () => { try { await createCollectionViaApi(testCollectionAlias) - await createCollectionFeaturedItemViaApi(testCollectionAlias, { + await createCollectionCustomFeaturedItemViaApi(testCollectionAlias, { content: '

Test content

', displayOrder: 1, withFile: true, fileName: 'featured-item-test-image.png' }) - await createCollectionFeaturedItemViaApi(testCollectionAlias, { + await createCollectionCustomFeaturedItemViaApi(testCollectionAlias, { content: '

Test content 2

', displayOrder: 2, withFile: false }) - await createCollectionFeaturedItemViaApi(testCollectionAlias, { + await createCollectionCustomFeaturedItemViaApi(testCollectionAlias, { content: '

Test content 3

', displayOrder: 3, withFile: false diff --git a/test/functional/collections/GetCollectionFeaturedItems.test.ts b/test/functional/collections/GetCollectionFeaturedItems.test.ts index a2f75a70..df7af77e 100644 --- a/test/functional/collections/GetCollectionFeaturedItems.test.ts +++ b/test/functional/collections/GetCollectionFeaturedItems.test.ts @@ -2,18 +2,27 @@ import { ApiConfig, ReadError, getCollectionFeaturedItems } from '../../../src' import { TestConstants } from '../../testHelpers/TestConstants' import { DataverseApiAuthMechanism } from '../../../src/core/infra/repositories/ApiConfig' import { - createCollectionFeaturedItemViaApi, + createCollectionCustomFeaturedItemViaApi, + createCollectionDvObjectFeaturedItemViaApi, + deleteCollectionFeaturedItemsViaApi, deleteCollectionFeaturedItemViaApi } from '../../testHelpers/collections/collectionFeaturedItemsHelper' import { createCollectionViaApi, + publishCollectionViaApi, deleteCollectionViaApi } from '../../testHelpers/collections/collectionHelper' import { ROOT_COLLECTION_ID } from '../../../src/collections/domain/models/Collection' +import { + CustomFeaturedItem, + DvObjectFeaturedItem, + FeaturedItemType +} from '../../../src/collections/domain/models/FeaturedItem' describe('execute', () => { const testCollectionAlias = 'getCollectionsFeaturedItemsTest' - let testFeaturedItemId: number + const featuredCollectionAlias = 'featured-collection-test' + let testFeaturedItemIds: number[] = [] beforeEach(async () => { ApiConfig.init( @@ -27,26 +36,45 @@ describe('execute', () => { try { await createCollectionViaApi(testCollectionAlias) - const featuredItemCreated = await createCollectionFeaturedItemViaApi(testCollectionAlias, { - content: '

Test content

', - displayOrder: 1, - withFile: true, - fileName: 'featured-item-test-image.png' - }) + await createCollectionViaApi(featuredCollectionAlias, testCollectionAlias) - testFeaturedItemId = featuredItemCreated.id + // Publish the collection to be featured otherwise it cannot be featured + await publishCollectionViaApi(testCollectionAlias) + await publishCollectionViaApi(featuredCollectionAlias) + + const featuredItemCreated = await createCollectionCustomFeaturedItemViaApi( + testCollectionAlias, + { + content: '

Test content

', + displayOrder: 1, + withFile: true, + fileName: 'featured-item-test-image.png' + } + ) + + const dvObjectFeaturedItemCreated = await createCollectionDvObjectFeaturedItemViaApi( + testCollectionAlias, + { + type: 'dataverse', + dvObjectIdentifier: featuredCollectionAlias, + displayOrder: 2 + } + ) + testFeaturedItemIds = [featuredItemCreated.id, dvObjectFeaturedItemCreated.id] } catch (error) { + console.dir(error) throw new Error(`Error while creating collection featured item in ${testCollectionAlias}`) } }) afterAll(async () => { try { - await deleteCollectionFeaturedItemViaApi(testFeaturedItemId) + await deleteCollectionFeaturedItemsViaApi(testCollectionAlias) + await deleteCollectionViaApi(featuredCollectionAlias) await deleteCollectionViaApi(testCollectionAlias) } catch (error) { throw new Error( - `Tests afterAll(): Error while deleting featured item with id ${testFeaturedItemId}` + `Tests afterAll(): Error while deleting featured item with id ${testFeaturedItemIds}` ) } }) @@ -54,30 +82,44 @@ describe('execute', () => { test('should return featured items array given a valid collection alias that has featured items', async () => { const featuredItemsResponse = await getCollectionFeaturedItems.execute(testCollectionAlias) - expect(featuredItemsResponse.length).toBe(1) - expect(featuredItemsResponse[0].id).toBe(testFeaturedItemId) - expect(featuredItemsResponse[0].displayOrder).toBe(1) - expect(featuredItemsResponse[0].content).toBe('

Test content

') - expect(featuredItemsResponse[0].imageFileUrl).toBe( - `http://localhost:8080/api/access/dataverseFeaturedItemImage/${featuredItemsResponse[0].id}` + const featuredItemOne = featuredItemsResponse[0] as CustomFeaturedItem + const featuredItemTwo = featuredItemsResponse[1] as DvObjectFeaturedItem + + expect(featuredItemsResponse.length).toBe(2) + expect(featuredItemOne.id).toBe(testFeaturedItemIds[0]) + expect(featuredItemOne.displayOrder).toBe(1) + expect(featuredItemOne.content).toBe('

Test content

') + expect(featuredItemOne.imageFileUrl).toContain( + `/api/access/dataverseFeaturedItemImage/${featuredItemOne.id}` ) - expect(featuredItemsResponse[0].imageFileName).toBe('featured-item-test-image.png') + expect(featuredItemOne.imageFileName).toBe('featured-item-test-image.png') + + expect(featuredItemTwo.id).toBe(testFeaturedItemIds[1]) + expect(featuredItemTwo.type).toBe(FeaturedItemType.COLLECTION) + expect(featuredItemTwo.dvObjectIdentifier).toBe(featuredCollectionAlias) + expect(featuredItemTwo.dvObjectDisplayName).toBe('Scientific Research') + expect(featuredItemTwo.displayOrder).toBe(2) }) it('should return imageFileUrl and imageFileName as undefined when featured item does not have an image', async () => { - const featuredItemCreated = await createCollectionFeaturedItemViaApi(testCollectionAlias, { - content: '

Test content

', - displayOrder: 2 - }) + const featuredItemCreated = await createCollectionCustomFeaturedItemViaApi( + testCollectionAlias, + { + content: '

Test content

', + displayOrder: 3 + } + ) const featuredItemsResponse = await getCollectionFeaturedItems.execute(testCollectionAlias) - expect(featuredItemsResponse.length).toBe(2) - expect(featuredItemsResponse[1].id).toBe(featuredItemCreated.id) - expect(featuredItemsResponse[1].displayOrder).toBe(2) - expect(featuredItemsResponse[1].content).toBe('

Test content

') - expect(featuredItemsResponse[1].imageFileUrl).toBeUndefined() - expect(featuredItemsResponse[1].imageFileName).toBeUndefined() + const featuredItemTwo = featuredItemsResponse[2] as CustomFeaturedItem + + expect(featuredItemsResponse.length).toBe(3) + expect(featuredItemTwo.id).toBe(featuredItemCreated.id) + expect(featuredItemTwo.displayOrder).toBe(3) + expect(featuredItemTwo.content).toBe('

Test content

') + expect(featuredItemTwo.imageFileUrl).toBeUndefined() + expect(featuredItemTwo.imageFileName).toBeUndefined() await deleteCollectionFeaturedItemViaApi(featuredItemCreated.id) }) diff --git a/test/functional/collections/UpdateCollectionFeaturedItems.test.ts b/test/functional/collections/UpdateCollectionFeaturedItems.test.ts index 2c196c63..87d3ebec 100644 --- a/test/functional/collections/UpdateCollectionFeaturedItems.test.ts +++ b/test/functional/collections/UpdateCollectionFeaturedItems.test.ts @@ -1,13 +1,16 @@ import { ApiConfig, - CollectionFeaturedItemsDTO, + FeaturedItemsDTO, + createDataset, + CreatedDatasetIdentifiers, + getDatasetFiles, updateCollectionFeaturedItems, WriteError } from '../../../src' import { TestConstants } from '../../testHelpers/TestConstants' import { DataverseApiAuthMechanism } from '../../../src/core/infra/repositories/ApiConfig' import { - createCollectionFeaturedItemViaApi, + createCollectionCustomFeaturedItemViaApi, createImageFile, deleteCollectionFeaturedItemViaApi } from '../../testHelpers/collections/collectionFeaturedItemsHelper' @@ -15,11 +18,28 @@ import { CONTENT_FIELD_WITH_ALL_TAGS, createCollectionViaApi, deleteCollectionViaApi, - EXPECTED_CONTENT_FIELD_WITH_ALL_TAGS + EXPECTED_CONTENT_FIELD_WITH_ALL_TAGS, + publishCollectionViaApi } from '../../testHelpers/collections/collectionHelper' +import { + CustomFeaturedItem, + DvObjectFeaturedItem, + FeaturedItemType +} from '../../../src/collections/domain/models/FeaturedItem' +import { uploadFileViaApi } from '../../testHelpers/files/filesHelper' +import { + deletePublishedDatasetViaApi, + publishDatasetViaApi, + waitForNoLocks +} from '../../testHelpers/datasets/datasetHelper' describe('execute', () => { const testCollectionAlias = 'updateCollectionFeaturedItemsTest' + const featuredCollectionAlias = 'featured-collection-test-1' + const testTextFile1Name = 'test-file-1.txt' + + let testDatasetIds: CreatedDatasetIdentifiers + let featuredFileId: number beforeEach(async () => { ApiConfig.init( @@ -30,44 +50,94 @@ describe('execute', () => { }) beforeAll(async () => { + ApiConfig.init( + TestConstants.TEST_API_URL, + DataverseApiAuthMechanism.API_KEY, + process.env.TEST_API_KEY + ) + try { await createCollectionViaApi(testCollectionAlias) + await createCollectionViaApi(featuredCollectionAlias, testCollectionAlias) + + // Publish the collection to be featured otherwise it cannot be featured + await publishCollectionViaApi(testCollectionAlias) + await publishCollectionViaApi(featuredCollectionAlias) + + // Create a dataset to be featured + try { + testDatasetIds = await createDataset.execute( + TestConstants.TEST_NEW_DATASET_DTO, + testCollectionAlias + ) + } catch (error) { + throw new Error('Tests beforeAll(): Error while creating test dataset') + } + // Create a file to be featured + await uploadFileViaApi(testDatasetIds.numericId, testTextFile1Name) + + // Get the file id + try { + const datasetFiles = await getDatasetFiles.execute(testDatasetIds.numericId) + featuredFileId = datasetFiles.files[0].id + } catch (error) { + throw new Error('Tests beforeAll(): Error while getting dataset files') + } + + await publishDatasetViaApi(testDatasetIds.numericId) + await waitForNoLocks(testDatasetIds.numericId, 10) } catch (error) { - throw new Error( - `Tests beforeAll(): Error while creating test collection: ${testCollectionAlias}` - ) + throw new Error('Tests beforeAll(): Error while creating test data') } }) afterAll(async () => { try { + await deletePublishedDatasetViaApi(testDatasetIds.persistentId) + await deleteCollectionViaApi(featuredCollectionAlias) await deleteCollectionViaApi(testCollectionAlias) } catch (error) { - throw new Error( - `Tests afterAll(): Error while deleting test collection: ${testCollectionAlias}` - ) + throw new Error('Tests afterAll(): Error while deleting test data') } }) it('should successfully update the featured items of a collection', async () => { - const newFeaturedItems: CollectionFeaturedItemsDTO = [ + const newFeaturedItems: FeaturedItemsDTO = [ { + type: FeaturedItemType.CUSTOM, content: '

Test content 1

', displayOrder: 0, file: undefined, keepFile: false }, { + type: FeaturedItemType.CUSTOM, content: '

Test content 2

', displayOrder: 1, file: undefined, keepFile: false }, { + type: FeaturedItemType.CUSTOM, content: CONTENT_FIELD_WITH_ALL_TAGS, displayOrder: 2, file: createImageFile('featured-item-test-image-3.png'), keepFile: false + }, + { + type: FeaturedItemType.COLLECTION, + dvObjectIdentifier: featuredCollectionAlias, + displayOrder: 3 + }, + { + type: FeaturedItemType.DATASET, + dvObjectIdentifier: testDatasetIds.persistentId, + displayOrder: 4 + }, + { + type: FeaturedItemType.FILE, + dvObjectIdentifier: featuredFileId.toString(), + displayOrder: 5 } ] @@ -76,41 +146,71 @@ describe('execute', () => { newFeaturedItems ) - expect(updatedFeaturedItemsResponse.length).toBe(3) - - expect(updatedFeaturedItemsResponse[0].content).toBe(newFeaturedItems[0].content) - expect(updatedFeaturedItemsResponse[0].displayOrder).toBe(newFeaturedItems[0].displayOrder) - expect(updatedFeaturedItemsResponse[0].imageFileUrl).toBeUndefined() - expect(updatedFeaturedItemsResponse[0].imageFileName).toBeUndefined() + const firstItemResponse = updatedFeaturedItemsResponse[0] as CustomFeaturedItem + const secondItemResponse = updatedFeaturedItemsResponse[1] as CustomFeaturedItem + const thirdItemResponse = updatedFeaturedItemsResponse[2] as CustomFeaturedItem + const fourthItemResponse = updatedFeaturedItemsResponse[3] as DvObjectFeaturedItem + const fifthItemResponse = updatedFeaturedItemsResponse[4] as DvObjectFeaturedItem + const sixthItemResponse = updatedFeaturedItemsResponse[5] as DvObjectFeaturedItem + + expect(updatedFeaturedItemsResponse.length).toBe(6) + + expect(firstItemResponse.content).toBe((newFeaturedItems[0] as CustomFeaturedItem).content) + expect(firstItemResponse.displayOrder).toBe(newFeaturedItems[0].displayOrder) + expect(firstItemResponse.imageFileUrl).toBeUndefined() + expect(firstItemResponse.imageFileName).toBeUndefined() + + expect(secondItemResponse.content).toBe((newFeaturedItems[1] as CustomFeaturedItem).content) + expect(secondItemResponse.displayOrder).toBe(newFeaturedItems[1].displayOrder) + expect(secondItemResponse.imageFileUrl).toBeUndefined() + expect(secondItemResponse.imageFileName).toBeUndefined() + + expect(thirdItemResponse.content).toEqual(EXPECTED_CONTENT_FIELD_WITH_ALL_TAGS) + expect(thirdItemResponse.displayOrder).toBe(newFeaturedItems[2].displayOrder) + expect(thirdItemResponse.imageFileName).toEqual('featured-item-test-image-3.png') + expect(thirdItemResponse.imageFileUrl).toContain( + `/api/access/dataverseFeaturedItemImage/${updatedFeaturedItemsResponse[2].id}` + ) - expect(updatedFeaturedItemsResponse[1].content).toBe(newFeaturedItems[1].content) - expect(updatedFeaturedItemsResponse[1].displayOrder).toBe(newFeaturedItems[1].displayOrder) - expect(updatedFeaturedItemsResponse[1].imageFileUrl).toBeUndefined() - expect(updatedFeaturedItemsResponse[1].imageFileName).toBeUndefined() + expect(fourthItemResponse.type).toBe(FeaturedItemType.COLLECTION) + expect(fourthItemResponse.dvObjectIdentifier).toBe(featuredCollectionAlias) + expect(fourthItemResponse.dvObjectDisplayName).toBe('Scientific Research') + expect(fourthItemResponse.displayOrder).toBe(newFeaturedItems[3].displayOrder) + expect(fourthItemResponse.id).toBeDefined() - expect(updatedFeaturedItemsResponse[2].content).toEqual(EXPECTED_CONTENT_FIELD_WITH_ALL_TAGS) - expect(updatedFeaturedItemsResponse[2].displayOrder).toBe(newFeaturedItems[2].displayOrder) - expect(updatedFeaturedItemsResponse[2].imageFileName).toEqual('featured-item-test-image-3.png') - expect(updatedFeaturedItemsResponse[2].imageFileUrl).toBe( - `http://localhost:8080/api/access/dataverseFeaturedItemImage/${updatedFeaturedItemsResponse[2].id}` + expect(fifthItemResponse.type).toBe(FeaturedItemType.DATASET) + expect(fifthItemResponse.dvObjectIdentifier).toBe(testDatasetIds.persistentId) + expect(fifthItemResponse.dvObjectDisplayName).toBe( + 'Dataset created using the createDataset use case' ) + expect(fifthItemResponse.displayOrder).toBe(newFeaturedItems[4].displayOrder) + expect(fifthItemResponse.id).toBeDefined() + + expect(sixthItemResponse.type).toBe(FeaturedItemType.FILE) + expect(sixthItemResponse.dvObjectIdentifier).toBe(featuredFileId.toString()) + expect(sixthItemResponse.dvObjectDisplayName).toBe(testTextFile1Name) + expect(sixthItemResponse.displayOrder).toBe(newFeaturedItems[5].displayOrder) + expect(sixthItemResponse.id).toBeDefined() }) test('should throw an error when collection does not exist', async () => { - const newFeaturedItems: CollectionFeaturedItemsDTO = [ + const newFeaturedItems: FeaturedItemsDTO = [ { + type: FeaturedItemType.CUSTOM, content: '

Test content 1

', displayOrder: 0, file: undefined, keepFile: false }, { + type: FeaturedItemType.CUSTOM, content: '

Test content 2

', displayOrder: 1, file: undefined, keepFile: false }, { + type: FeaturedItemType.CUSTOM, content: '

Test content 3

', displayOrder: 2, file: createImageFile('featured-item-test-image-3.png'), @@ -133,8 +233,9 @@ describe('execute', () => { }) test('should throw an error when featured item content is empty', async () => { - const newFeaturedItems: CollectionFeaturedItemsDTO = [ + const newFeaturedItems: FeaturedItemsDTO = [ { + type: FeaturedItemType.CUSTOM, content: '', displayOrder: 0, file: undefined, @@ -163,12 +264,15 @@ describe('execute', () => { beforeEach(async () => { try { - const featuredItemCreated = await createCollectionFeaturedItemViaApi(testCollectionAlias, { - content: testFeaturedItemContent, - displayOrder: 1, - withFile: true, - fileName: testFeaturedItemFilename - }) + const featuredItemCreated = await createCollectionCustomFeaturedItemViaApi( + testCollectionAlias, + { + content: testFeaturedItemContent, + displayOrder: 1, + withFile: true, + fileName: testFeaturedItemFilename + } + ) testFeaturedItemId = featuredItemCreated.id } catch (error) { @@ -187,9 +291,10 @@ describe('execute', () => { }) it('should keep existing file for a featured item if file is undefined and keepFile is true', async () => { - const newFeaturedItems: CollectionFeaturedItemsDTO = [ + const newFeaturedItems: FeaturedItemsDTO = [ { id: testFeaturedItemId, + type: FeaturedItemType.CUSTOM, content: '

Test content Updated

', displayOrder: 0, file: undefined, @@ -204,19 +309,22 @@ describe('execute', () => { expect(updatedFeaturedItemsResponse.length).toBe(1) - expect(updatedFeaturedItemsResponse[0].content).toBe(newFeaturedItems[0].content) - expect(updatedFeaturedItemsResponse[0].displayOrder).toBe(newFeaturedItems[0].displayOrder) + const updatedFeaturedItem = updatedFeaturedItemsResponse[0] as CustomFeaturedItem + + expect(updatedFeaturedItem.content).toBe((newFeaturedItems[0] as CustomFeaturedItem).content) + expect(updatedFeaturedItem.displayOrder).toBe(newFeaturedItems[0].displayOrder) // Should keep the existing file even if a file was not provided because keepFile is true - expect(updatedFeaturedItemsResponse[0].imageFileName).toEqual(testFeaturedItemFilename) - expect(updatedFeaturedItemsResponse[0].imageFileUrl).toBe( - `http://localhost:8080/api/access/dataverseFeaturedItemImage/${updatedFeaturedItemsResponse[0].id}` + expect(updatedFeaturedItem.imageFileName).toEqual(testFeaturedItemFilename) + expect(updatedFeaturedItem.imageFileUrl).toContain( + `/api/access/dataverseFeaturedItemImage/${updatedFeaturedItem.id}` ) }) it('should remove existing file for a featured item if file is undefined and keepFile is false', async () => { - const newFeaturedItems: CollectionFeaturedItemsDTO = [ + const newFeaturedItems: FeaturedItemsDTO = [ { id: testFeaturedItemId, + type: FeaturedItemType.CUSTOM, content: '

Test content Updated

', displayOrder: 0, file: undefined, @@ -231,16 +339,19 @@ describe('execute', () => { expect(updatedFeaturedItemsResponse.length).toBe(1) - expect(updatedFeaturedItemsResponse[0].content).toBe(newFeaturedItems[0].content) - expect(updatedFeaturedItemsResponse[0].displayOrder).toBe(newFeaturedItems[0].displayOrder) - expect(updatedFeaturedItemsResponse[0].imageFileUrl).toBeUndefined() - expect(updatedFeaturedItemsResponse[0].imageFileName).toBeUndefined() + const updatedFeaturedItem = updatedFeaturedItemsResponse[0] as CustomFeaturedItem + + expect(updatedFeaturedItem.content).toBe((newFeaturedItems[0] as CustomFeaturedItem).content) + expect(updatedFeaturedItem.displayOrder).toBe(newFeaturedItems[0].displayOrder) + expect(updatedFeaturedItem.imageFileUrl).toBeUndefined() + expect(updatedFeaturedItem.imageFileName).toBeUndefined() }) it('should replace existing file for a featured item if a new file is provided and keepFile is false', async () => { - const newFeaturedItems: CollectionFeaturedItemsDTO = [ + const newFeaturedItems: FeaturedItemsDTO = [ { id: testFeaturedItemId, + type: FeaturedItemType.CUSTOM, content: '

Test content Updated

', displayOrder: 0, file: createImageFile('featured-item-test-image-updated.png'), @@ -255,20 +366,21 @@ describe('execute', () => { expect(updatedFeaturedItemsResponse.length).toBe(1) - expect(updatedFeaturedItemsResponse[0].content).toBe(newFeaturedItems[0].content) - expect(updatedFeaturedItemsResponse[0].displayOrder).toBe(newFeaturedItems[0].displayOrder) - expect(updatedFeaturedItemsResponse[0].imageFileName).toEqual( - 'featured-item-test-image-updated.png' - ) - expect(updatedFeaturedItemsResponse[0].imageFileUrl).toBe( - `http://localhost:8080/api/access/dataverseFeaturedItemImage/${updatedFeaturedItemsResponse[0].id}` + const updatedFeaturedItem = updatedFeaturedItemsResponse[0] as CustomFeaturedItem + + expect(updatedFeaturedItem.content).toBe((newFeaturedItems[0] as CustomFeaturedItem).content) + expect(updatedFeaturedItem.displayOrder).toBe(newFeaturedItems[0].displayOrder) + expect(updatedFeaturedItem.imageFileName).toEqual('featured-item-test-image-updated.png') + expect(updatedFeaturedItem.imageFileUrl).toContain( + `/api/access/dataverseFeaturedItemImage/${updatedFeaturedItem.id}` ) }) it('should not replace existing file for a featured item if a new file is provided but keepFile is true', async () => { - const newFeaturedItems: CollectionFeaturedItemsDTO = [ + const newFeaturedItems: FeaturedItemsDTO = [ { id: testFeaturedItemId, + type: FeaturedItemType.CUSTOM, content: '

Test content Updated

', displayOrder: 0, file: createImageFile('featured-item-test-image-updated.png'), @@ -281,14 +393,16 @@ describe('execute', () => { newFeaturedItems ) + const testFeaturedItem = updatedFeaturedItemsResponse[0] as CustomFeaturedItem + expect(updatedFeaturedItemsResponse.length).toBe(1) - expect(updatedFeaturedItemsResponse[0].content).toBe(newFeaturedItems[0].content) - expect(updatedFeaturedItemsResponse[0].displayOrder).toBe(newFeaturedItems[0].displayOrder) + expect(testFeaturedItem.content).toBe((newFeaturedItems[0] as CustomFeaturedItem).content) + expect(testFeaturedItem.displayOrder).toBe(newFeaturedItems[0].displayOrder) // Should keep the existing file even if a file was provided because keepFile is true - expect(updatedFeaturedItemsResponse[0].imageFileName).toEqual(testFeaturedItemFilename) - expect(updatedFeaturedItemsResponse[0].imageFileUrl).toBe( - `http://localhost:8080/api/access/dataverseFeaturedItemImage/${updatedFeaturedItemsResponse[0].id}` + expect(testFeaturedItem.imageFileName).toEqual(testFeaturedItemFilename) + expect(testFeaturedItem.imageFileUrl).toContain( + `/api/access/dataverseFeaturedItemImage/${updatedFeaturedItemsResponse[0].id}` ) }) }) diff --git a/test/integration/collections/CollectionsRepository.test.ts b/test/integration/collections/CollectionsRepository.test.ts index b741537a..d869834b 100644 --- a/test/integration/collections/CollectionsRepository.test.ts +++ b/test/integration/collections/CollectionsRepository.test.ts @@ -2,7 +2,6 @@ import { CollectionsRepository } from '../../../src/collections/infra/repositori import { TestConstants } from '../../testHelpers/TestConstants' import { CollectionDTO, - CollectionFeaturedItemsDTO, CollectionItemType, CollectionPreview, CollectionSearchCriteria, @@ -13,7 +12,10 @@ import { WriteError, createDataset, getCollection, - createCollection + createCollection, + getDatasetFiles, + restrictFile, + deleteFile } from '../../../src' import { ApiConfig } from '../../../src' import { DataverseApiAuthMechanism } from '../../../src/core/infra/repositories/ApiConfig' @@ -21,6 +23,7 @@ import { createCollectionDTO, createCollectionViaApi, deleteCollectionViaApi, + publishCollectionViaApi, ROOT_COLLECTION_ALIAS } from '../../testHelpers/collections/collectionHelper' import { CollectionPayload } from '../../../src/collections/infra/repositories/transformers/CollectionPayload' @@ -28,7 +31,8 @@ import { updateFileTabularTags, uploadFileViaApi } from '../../testHelpers/files import { deletePublishedDatasetViaApi, deleteUnpublishedDatasetViaApi, - publishDatasetViaApi + publishDatasetViaApi, + waitForNoLocks } from '../../testHelpers/datasets/datasetHelper' import { PublicationStatus } from '../../../src/core/domain/models/PublicationStatus' import { CollectionType } from '../../../src/collections/domain/models/CollectionType' @@ -38,12 +42,21 @@ import { } from '../../../src/collections/domain/models/CollectionSearchCriteria' import { ROOT_COLLECTION_ID } from '../../../src/collections/domain/models/Collection' import { - createCollectionFeaturedItemViaApi, + createCollectionCustomFeaturedItemViaApi, + createCollectionDvObjectFeaturedItemViaApi, createImageFile, deleteCollectionFeaturedItemsViaApi, deleteCollectionFeaturedItemViaApi } from '../../testHelpers/collections/collectionFeaturedItemsHelper' import { createApiTokenViaApi } from '../../testHelpers/users/apiTokenHelper' +import { + CustomFeaturedItem, + FeaturedItemType +} from '../../../src/collections/domain/models/FeaturedItem' +import { + DvObjectFeaturedItemDTO, + FeaturedItemsDTO +} from '../../../src/collections/domain/dtos/FeaturedItemsDTO' describe('CollectionsRepository', () => { const testCollectionAlias = 'collectionsRepositoryTestCollection' @@ -1123,33 +1136,6 @@ describe('CollectionsRepository', () => { }) describe('getCollectionFeaturedItems', () => { - let testFeaturedItemId: number - - beforeAll(async () => { - try { - const featuredItemCreated = await createCollectionFeaturedItemViaApi(testCollectionAlias, { - content: '

Test content

', - displayOrder: 1, - withFile: true, - fileName: 'featured-item-test-image.png' - }) - - testFeaturedItemId = featuredItemCreated.id - } catch (error) { - throw new Error(`Error while creating collection featured item in ${testCollectionAlias}`) - } - }) - - afterAll(async () => { - try { - await deleteCollectionFeaturedItemViaApi(testFeaturedItemId) - } catch (error) { - throw new Error( - `Tests afterAll(): Error while deleting featured item with id ${testFeaturedItemId}` - ) - } - }) - test('should return empty featured items array given a valid collection alias when collection has no featured items', async () => { const featuredItemsResponse = await sut.getCollectionFeaturedItems(ROOT_COLLECTION_ID) @@ -1157,16 +1143,29 @@ describe('CollectionsRepository', () => { }) test('should return featured items array given a valid collection alias when collection has featured items', async () => { + const featuredItemCreated = await createCollectionCustomFeaturedItemViaApi( + testCollectionAlias, + { + content: '

Test content

', + displayOrder: 1, + withFile: true, + fileName: 'featured-item-test-image.png' + } + ) + const featuredItemsResponse = await sut.getCollectionFeaturedItems(testCollectionAlias) expect(featuredItemsResponse.length).toBe(1) - expect(featuredItemsResponse[0].id).toBe(testFeaturedItemId) - expect(featuredItemsResponse[0].displayOrder).toBe(1) - expect(featuredItemsResponse[0].content).toBe('

Test content

') - expect(featuredItemsResponse[0].imageFileUrl).toBe( - `http://localhost:8080/api/access/dataverseFeaturedItemImage/${featuredItemsResponse[0].id}` + const firstFeaturedItem = featuredItemsResponse[0] as CustomFeaturedItem + expect(firstFeaturedItem.id).toBe(featuredItemCreated.id) + expect(firstFeaturedItem.displayOrder).toBe(1) + expect(firstFeaturedItem.content).toBe('

Test content

') + expect(firstFeaturedItem.imageFileUrl).toContain( + `/api/access/dataverseFeaturedItemImage/${firstFeaturedItem.id}` ) - expect(featuredItemsResponse[0].imageFileName).toBe('featured-item-test-image.png') + expect(firstFeaturedItem.imageFileName).toBe('featured-item-test-image.png') + + await deleteCollectionFeaturedItemViaApi(featuredItemCreated.id) }) test('should return error when collection does not exist', async () => { @@ -1179,6 +1178,102 @@ describe('CollectionsRepository', () => { expectedError ) }) + + test('Featured Item type File should not be returned if the file was deleted and the dataset published', async () => { + const testFileDeletedCollectionAlias = 'testCollectionFeaturedItemsFileDeletion' + await createCollectionViaApi(testFileDeletedCollectionAlias) + await publishCollectionViaApi(testFileDeletedCollectionAlias) + const testDatasetIds = await createDataset.execute( + TestConstants.TEST_NEW_DATASET_DTO, + testFileDeletedCollectionAlias + ) + await uploadFileViaApi(testDatasetIds.numericId, 'test-file-1.txt') + await publishDatasetViaApi(testDatasetIds.numericId) + await waitForNoLocks(testDatasetIds.numericId, 10) + + const datasetFiles = await getDatasetFiles.execute(testDatasetIds.numericId) + const fileId = datasetFiles.files[0].id + + await createCollectionDvObjectFeaturedItemViaApi(testFileDeletedCollectionAlias, { + type: 'datafile', + dvObjectIdentifier: fileId.toString(), + displayOrder: 0 + }) + + const featuredItemsResponse = await sut.getCollectionFeaturedItems( + testFileDeletedCollectionAlias + ) + + expect(featuredItemsResponse.length).toBe(1) + + // Now we delete the file + await deleteFile.execute(fileId) + + // If we dont publish the dataset the featured item will still be there + const featuredItemsResponseAfterFileDeletion = await sut.getCollectionFeaturedItems( + testFileDeletedCollectionAlias + ) + + expect(featuredItemsResponseAfterFileDeletion.length).toBe(1) + + // Once we publish the dataset the featured item will not be returned anymore + await publishDatasetViaApi(testDatasetIds.numericId) + await waitForNoLocks(testDatasetIds.numericId, 10) + + const featuredItemsResponseAfterDatasetPublish = await sut.getCollectionFeaturedItems( + testFileDeletedCollectionAlias + ) + + expect(featuredItemsResponseAfterDatasetPublish.length).toBe(0) + + await deletePublishedDatasetViaApi(testDatasetIds.persistentId) + await deleteCollectionViaApi(testFileDeletedCollectionAlias) + }) + + test('Featured Item type File should not be returned if the file was restricted', async () => { + const testFileRestrictedCollectionAlias = 'testCollectionFeaturedItemsFileRestriction' + await createCollectionViaApi(testFileRestrictedCollectionAlias) + await publishCollectionViaApi(testFileRestrictedCollectionAlias) + const testDatasetIds = await createDataset.execute( + TestConstants.TEST_NEW_DATASET_DTO, + testFileRestrictedCollectionAlias + ) + await uploadFileViaApi(testDatasetIds.numericId, 'test-file-1.txt') + await publishDatasetViaApi(testDatasetIds.numericId) + await waitForNoLocks(testDatasetIds.numericId, 10) + + const datasetFiles = await getDatasetFiles.execute(testDatasetIds.numericId) + const fileId = datasetFiles.files[0].id + + await createCollectionDvObjectFeaturedItemViaApi(testFileRestrictedCollectionAlias, { + type: 'datafile', + dvObjectIdentifier: fileId.toString(), + displayOrder: 0 + }) + + const featuredItemsResponse = await sut.getCollectionFeaturedItems( + testFileRestrictedCollectionAlias + ) + + expect(featuredItemsResponse.length).toBe(1) + + // Now we restrict the file + await restrictFile.execute(fileId, { + restrict: true, + enableAccessRequest: true, + termsOfAccess: 'This file is restricted for testing purposes' + }) + + // Restricted file should not be returned as featured item once restricted + const featuredItemsResponseAfterFileRestriction = await sut.getCollectionFeaturedItems( + testFileRestrictedCollectionAlias + ) + + expect(featuredItemsResponseAfterFileRestriction.length).toBe(0) + + await deletePublishedDatasetViaApi(testDatasetIds.persistentId) + await deleteCollectionViaApi(testFileRestrictedCollectionAlias) + }) }) describe('updateCollectionFeaturedItems', () => { @@ -1193,20 +1288,23 @@ describe('CollectionsRepository', () => { }) it('should update collection featured items sending all new items', async () => { - const newFeaturedItems: CollectionFeaturedItemsDTO = [ + const newFeaturedItems: FeaturedItemsDTO = [ { + type: FeaturedItemType.CUSTOM, content: '

Test content 1

', displayOrder: 0, file: undefined, keepFile: false }, { + type: FeaturedItemType.CUSTOM, content: '

Test content 2

', displayOrder: 1, file: undefined, keepFile: false }, { + type: FeaturedItemType.CUSTOM, content: '

Test content 3

', displayOrder: 2, file: createImageFile('featured-item-test-image-3.png'), @@ -1221,40 +1319,201 @@ describe('CollectionsRepository', () => { expect(response).toHaveLength(3) - expect(response[0].content).toEqual(newFeaturedItems[0].content) - expect(response[0].displayOrder).toEqual(newFeaturedItems[0].displayOrder) - expect(response[0].imageFileName).toEqual(undefined) - expect(response[0].imageFileUrl).toEqual(undefined) + const firstFeaturedItem = response[0] as CustomFeaturedItem + const secondFeaturedItem = response[1] as CustomFeaturedItem + const thirdFeaturedItem = response[2] as CustomFeaturedItem + + expect(firstFeaturedItem.content).toEqual((newFeaturedItems[0] as CustomFeaturedItem).content) + expect(firstFeaturedItem.displayOrder).toEqual(newFeaturedItems[0].displayOrder) + expect(firstFeaturedItem.imageFileName).toEqual(undefined) + expect(firstFeaturedItem.imageFileUrl).toEqual(undefined) + + expect(secondFeaturedItem.content).toEqual( + (newFeaturedItems[1] as CustomFeaturedItem).content + ) + expect(secondFeaturedItem.displayOrder).toEqual(newFeaturedItems[1].displayOrder) + expect(secondFeaturedItem.imageFileName).toEqual(undefined) + expect(secondFeaturedItem.imageFileUrl).toEqual(undefined) + + expect(thirdFeaturedItem.content).toEqual((newFeaturedItems[2] as CustomFeaturedItem).content) + expect(thirdFeaturedItem.displayOrder).toEqual(newFeaturedItems[2].displayOrder) + expect(thirdFeaturedItem.imageFileName).toEqual('featured-item-test-image-3.png') + expect(thirdFeaturedItem.imageFileUrl).toContain( + `/api/access/dataverseFeaturedItemImage/${response[2].id}` + ) + }) + + it('should return error when the dvObjectIdentifier of a collection does not exist', async () => { + const invalidCollectionAlias = 'invalid-collection-alias' + const newFeaturedItems: DvObjectFeaturedItemDTO[] = [ + { + type: FeaturedItemType.COLLECTION, + dvObjectIdentifier: invalidCollectionAlias, + displayOrder: 0 + } + ] + + const expectedError = new WriteError( + `[400] Cant find Collection, Dataset, or Datafile with identifier: ${invalidCollectionAlias}.` + ) + await expect( + sut.updateCollectionFeaturedItems(testCollectionAlias, newFeaturedItems) + ).rejects.toThrow(expectedError) + }) + + it('should return error when the dvObjectIdentifier of a dataset does not exist', async () => { + const invalidDatasetPersistentId = 'doi:10.5072/FK2/INVALID_DATASET' + const newFeaturedItems: DvObjectFeaturedItemDTO[] = [ + { + type: FeaturedItemType.DATASET, + dvObjectIdentifier: invalidDatasetPersistentId, + displayOrder: 0 + } + ] + const expectedError = new WriteError( + `[400] Cant find Collection, Dataset, or Datafile with identifier: ${invalidDatasetPersistentId}.` + ) + await expect( + sut.updateCollectionFeaturedItems(testCollectionAlias, newFeaturedItems) + ).rejects.toThrow(expectedError) + }) + + it('should return error when the dvObjectIdentifier of a file does not exist', async () => { + const invalidFileId = '99' + const newFeaturedItems: DvObjectFeaturedItemDTO[] = [ + { + type: FeaturedItemType.FILE, + dvObjectIdentifier: invalidFileId, + displayOrder: 0 + } + ] + const expectedError = new WriteError( + `[400] Cant find Collection, Dataset, or Datafile with identifier: ${invalidFileId}.` + ) + await expect( + sut.updateCollectionFeaturedItems(testCollectionAlias, newFeaturedItems) + ).rejects.toThrow(expectedError) + }) + + it('should return error when the collection to feature is not published', async () => { + const unpublishedCollectionAlias = 'unpublished-collection-featured-item-test' + await createCollectionViaApi(unpublishedCollectionAlias, testCollectionAlias) + + const newFeaturedItems: DvObjectFeaturedItemDTO[] = [ + { + type: FeaturedItemType.COLLECTION, + dvObjectIdentifier: unpublishedCollectionAlias, + displayOrder: 0 + } + ] + const expectedError = new WriteError('[400] Dataverse must be published to be featured.') + await expect( + sut.updateCollectionFeaturedItems(testCollectionAlias, newFeaturedItems) + ).rejects.toThrow(expectedError) + await deleteCollectionViaApi(unpublishedCollectionAlias) + }) + + it('should return error when the dataset to feature is not published', async () => { + const testDatasetIds = await createDataset.execute( + TestConstants.TEST_NEW_DATASET_DTO, + testCollectionAlias + ) + + const newFeaturedItems: DvObjectFeaturedItemDTO[] = [ + { + type: FeaturedItemType.DATASET, + dvObjectIdentifier: testDatasetIds.persistentId, + displayOrder: 0 + } + ] + const expectedError = new WriteError('[400] Dataset must be published to be featured.') + await expect( + sut.updateCollectionFeaturedItems(testCollectionAlias, newFeaturedItems) + ).rejects.toThrow(expectedError) + + await deleteUnpublishedDatasetViaApi(testDatasetIds.numericId) + }) + + it('should return error when the file to feature is not published', async () => { + const testDatasetIds = await createDataset.execute( + TestConstants.TEST_NEW_DATASET_DTO, + testCollectionAlias + ) + await uploadFileViaApi(testDatasetIds.numericId, 'test-file-1.txt') + + const datasetFiles = await getDatasetFiles.execute(testDatasetIds.numericId) + + const fileId = datasetFiles.files[0].id + + const newFeaturedItems: DvObjectFeaturedItemDTO[] = [ + { + type: FeaturedItemType.FILE, + dvObjectIdentifier: fileId.toString(), + displayOrder: 0 + } + ] + + const expectedError = new WriteError('[400] Dataset must be published to be featured.') + + await expect( + sut.updateCollectionFeaturedItems(testCollectionAlias, newFeaturedItems) + ).rejects.toThrow(expectedError) - expect(response[1].content).toEqual(newFeaturedItems[1].content) - expect(response[1].displayOrder).toEqual(newFeaturedItems[1].displayOrder) - expect(response[1].imageFileName).toEqual(undefined) - expect(response[1].imageFileUrl).toEqual(undefined) + await deleteUnpublishedDatasetViaApi(testDatasetIds.numericId) + }) - expect(response[2].content).toEqual(newFeaturedItems[2].content) - expect(response[2].displayOrder).toEqual(newFeaturedItems[2].displayOrder) - expect(response[2].imageFileName).toEqual('featured-item-test-image-3.png') - expect(response[2].imageFileUrl).toBe( - `http://localhost:8080/api/access/dataverseFeaturedItemImage/${response[2].id}` + it('should return error when the file to feature is restricted', async () => { + const testDatasetIds = await createDataset.execute( + TestConstants.TEST_NEW_DATASET_DTO, + testCollectionAlias ) + await publishDatasetViaApi(testDatasetIds.numericId) + await waitForNoLocks(testDatasetIds.numericId, 10) + await uploadFileViaApi(testDatasetIds.numericId, 'test-file-1.txt') + + const datasetFiles = await getDatasetFiles.execute(testDatasetIds.numericId) + + const fileId = datasetFiles.files[0].id + + await restrictFile.execute(fileId, { + restrict: true, + enableAccessRequest: true, + termsOfAccess: 'This file is restricted for testing purposes' + }) + + const newFeaturedItems: DvObjectFeaturedItemDTO[] = [ + { + type: FeaturedItemType.FILE, + dvObjectIdentifier: fileId.toString(), + displayOrder: 0 + } + ] + + const expectedError = new WriteError('[400] Datafile must not be restricted to be featured.') + + await expect( + sut.updateCollectionFeaturedItems(testCollectionAlias, newFeaturedItems) + ).rejects.toThrow(expectedError) + + await deletePublishedDatasetViaApi(testDatasetIds.persistentId) }) }) describe('deleteCollectionFeaturedItems', () => { beforeAll(async () => { try { - await createCollectionFeaturedItemViaApi(testCollectionAlias, { + await createCollectionCustomFeaturedItemViaApi(testCollectionAlias, { content: '

Test content

', displayOrder: 1, withFile: true, fileName: 'featured-item-test-image.png' }) - await createCollectionFeaturedItemViaApi(testCollectionAlias, { + await createCollectionCustomFeaturedItemViaApi(testCollectionAlias, { content: '

Test content 2

', displayOrder: 2, withFile: false }) - await createCollectionFeaturedItemViaApi(testCollectionAlias, { + await createCollectionCustomFeaturedItemViaApi(testCollectionAlias, { content: '

Test content 3

', displayOrder: 3, withFile: false @@ -1292,6 +1551,43 @@ describe('CollectionsRepository', () => { expect(featuredItemsResponseAfterDeletion).toStrictEqual([]) }) }) + + describe('deleteCollectionFeaturedItem', () => { + let featuredItemTestId: number + + beforeAll(async () => { + try { + const featuredItem = await createCollectionCustomFeaturedItemViaApi(testCollectionAlias, { + content: '

Test content

', + displayOrder: 1, + withFile: true, + fileName: 'featured-item-test-image.png' + }) + featuredItemTestId = featuredItem.id + } catch (error) { + throw new Error( + `Tests afterAll(): Error while creating test featured items for collection: ${testCollectionAlias}` + ) + } + }) + + it('should delete a collection featured item', async () => { + const featuredItemsResponseBeforeDeletion = await sut.getCollectionFeaturedItems( + testCollectionAlias + ) + + expect(featuredItemsResponseBeforeDeletion).toHaveLength(1) + + await sut.deleteCollectionFeaturedItem(featuredItemTestId) + + const featuredItemsResponseAfterDeletion = await sut.getCollectionFeaturedItems( + testCollectionAlias + ) + + expect(featuredItemsResponseAfterDeletion).toStrictEqual([]) + }) + }) + describe('getMyDataCollectionItems', () => { let testDatasetIds: CreatedDatasetIdentifiers diff --git a/test/testHelpers/collections/collectionFeaturedItemsHelper.ts b/test/testHelpers/collections/collectionFeaturedItemsHelper.ts index 48032389..57ef3980 100644 --- a/test/testHelpers/collections/collectionFeaturedItemsHelper.ts +++ b/test/testHelpers/collections/collectionFeaturedItemsHelper.ts @@ -1,32 +1,34 @@ import axios from 'axios' import { File, Blob } from '@web-std/file' -import { CollectionFeaturedItem } from '../../../src/collections/domain/models/CollectionFeaturedItem' +import { FeaturedItem, FeaturedItemType } from '../../../src/collections/domain/models/FeaturedItem' import { ROOT_COLLECTION_ID } from '../../../src/collections/domain/models/Collection' import { TestConstants } from '../TestConstants' -import { CollectionFeaturedItemsDTO } from '../../../src' +import { FeaturedItemsDTO } from '../../../src' +import { DvObjectFeaturedItemPayload } from '../../../src/collections/infra/repositories/transformers/CollectionFeaturedItemPayload' -interface CreateCollectionFeaturedItemData { +interface CreateCollectionCustomFeaturedItemData { content: string displayOrder?: number withFile?: boolean fileName?: string } -export async function createCollectionFeaturedItemViaApi( +export async function createCollectionCustomFeaturedItemViaApi( collectionAlias: string, { content, displayOrder = 1, withFile = false, fileName = 'test-image.png' - }: CreateCollectionFeaturedItemData -): Promise { + }: CreateCollectionCustomFeaturedItemData +): Promise { try { if (collectionAlias == undefined) { collectionAlias = ROOT_COLLECTION_ID } const formData = new FormData() + formData.append('type', 'custom') formData.append('content', content) formData.append('displayOrder', displayOrder.toString()) @@ -52,6 +54,42 @@ export async function createCollectionFeaturedItemViaApi( } } +interface CreateCollectionDvObjectFeaturedItemData { + type: DvObjectFeaturedItemPayload['type'] + dvObjectIdentifier: DvObjectFeaturedItemPayload['dvObjectIdentifier'] + displayOrder?: number +} + +export async function createCollectionDvObjectFeaturedItemViaApi( + collectionAlias: string, + { type, dvObjectIdentifier, displayOrder = 1 }: CreateCollectionDvObjectFeaturedItemData +): Promise { + try { + const formData = new FormData() + formData.append('type', type) + formData.append('content', '') + formData.append('displayOrder', displayOrder.toString()) + formData.append('dvObjectIdentifier', dvObjectIdentifier) + formData.append('content', '') + formData.append('keepFile', '') + formData.append('fileName', '') + + return await axios + .post(`${TestConstants.TEST_API_URL}/dataverses/${collectionAlias}/featuredItems`, formData, { + headers: { + 'Content-Type': 'multipart/form-data', + 'X-Dataverse-Key': process.env.TEST_API_KEY + } + }) + .then((response) => { + return response.data.data + }) + } catch (error) { + console.log(error) + throw new Error(`Error while creating collection featured item in ${collectionAlias}`) + } +} + export async function deleteCollectionFeaturedItemViaApi(featuredItemId: number): Promise { try { return await axios.delete( @@ -79,10 +117,11 @@ export async function deleteCollectionFeaturedItemsViaApi(collectionAlias: strin } } -export const createCollectionFeaturedItemsModel = (): CollectionFeaturedItem[] => { +export const createCollectionFeaturedItemsModel = (): FeaturedItem[] => { return [ { id: 1, + type: FeaturedItemType.CUSTOM, content: 'This is a featured item', displayOrder: 1, imageFileName: 'test-image.png', @@ -90,18 +129,27 @@ export const createCollectionFeaturedItemsModel = (): CollectionFeaturedItem[] = }, { id: 2, + type: FeaturedItemType.CUSTOM, content: 'This is another featured item', displayOrder: 2, imageFileName: undefined, imageFileUrl: undefined + }, + { + id: 3, + type: FeaturedItemType.DATASET, + displayOrder: 3, + dvObjectIdentifier: 'doi:10.5072/FK2/8YOKQI', + dvObjectDisplayName: 'Dataset Title' } ] } -export const createCollectionFeaturedItemsDTO = (): CollectionFeaturedItemsDTO => { +export const createCollectionFeaturedItemsDTO = (): FeaturedItemsDTO => { return [ { id: 1, + type: FeaturedItemType.CUSTOM, content: 'This is a featured item', displayOrder: 1, file: createImageFile(), @@ -109,10 +157,29 @@ export const createCollectionFeaturedItemsDTO = (): CollectionFeaturedItemsDTO = }, { id: 2, + type: FeaturedItemType.CUSTOM, content: 'This is another featured item', displayOrder: 2, file: undefined, keepFile: false + }, + { + id: 3, + type: FeaturedItemType.COLLECTION, + displayOrder: 3, + dvObjectIdentifier: 'collection-alias-foo-bar' + }, + { + id: 4, + type: FeaturedItemType.DATASET, + displayOrder: 4, + dvObjectIdentifier: 'doi:10.5072/FK2/8YOKQI' + }, + { + id: 5, + type: FeaturedItemType.FILE, + displayOrder: 5, + dvObjectIdentifier: '12' } ] } diff --git a/test/unit/collections/DeleteCollectionFeaturedItem.test.ts b/test/unit/collections/DeleteCollectionFeaturedItem.test.ts new file mode 100644 index 00000000..cf2501b7 --- /dev/null +++ b/test/unit/collections/DeleteCollectionFeaturedItem.test.ts @@ -0,0 +1,29 @@ +import { ICollectionsRepository } from '../../../src/collections/domain/repositories/ICollectionsRepository' +import { WriteError } from '../../../src' +import { DeleteCollectionFeaturedItem } from '../../../src/collections/domain/useCases/DeleteCollectionFeaturedItem' + +describe('execute', () => { + test('should return undefined on repository success', async () => { + const collectionRepositoryStub: ICollectionsRepository = {} as ICollectionsRepository + collectionRepositoryStub.deleteCollectionFeaturedItem = jest.fn().mockResolvedValue(undefined) + const testDeleteCollectionFeaturedItem = new DeleteCollectionFeaturedItem( + collectionRepositoryStub + ) + + const actual = await testDeleteCollectionFeaturedItem.execute(1) + + expect(actual).toEqual(undefined) + }) + + test('should return error result on repository error', async () => { + const collectionRepositoryStub: ICollectionsRepository = {} as ICollectionsRepository + collectionRepositoryStub.deleteCollectionFeaturedItem = jest + .fn() + .mockRejectedValue(new WriteError()) + const testDeleteCollectionFeaturedItem = new DeleteCollectionFeaturedItem( + collectionRepositoryStub + ) + + await expect(testDeleteCollectionFeaturedItem.execute(1)).rejects.toThrow(WriteError) + }) +})