From 1db9726305cfcfedd2d2bbf52e22f330df7cd356 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Germ=C3=A1n=20Saracca?= Date: Fri, 6 Jun 2025 11:24:00 -0300 Subject: [PATCH 01/16] initial work --- .../domain/dtos/CollectionFeaturedItemsDTO.ts | 14 ++- .../domain/models/CollectionFeaturedItem.ts | 12 +- .../repositories/CollectionsRepository.ts | 50 +++++++-- .../CollectionFeaturedItemPayload.ts | 12 +- .../collectionFeaturedItemsTransformer.ts | 58 ++++++++-- .../DeleteCollectionFeaturedItems.test.ts | 8 +- .../GetCollectionFeaturedItems.test.ts | 55 ++++++---- .../UpdateCollectionFeaturedItems.test.ts | 103 +++++++++++------- .../collections/CollectionsRepository.test.ts | 79 ++++++++------ .../collectionFeaturedItemsHelper.ts | 31 +++++- 10 files changed, 296 insertions(+), 126 deletions(-) diff --git a/src/collections/domain/dtos/CollectionFeaturedItemsDTO.ts b/src/collections/domain/dtos/CollectionFeaturedItemsDTO.ts index 7a60052d..66e5e54e 100644 --- a/src/collections/domain/dtos/CollectionFeaturedItemsDTO.ts +++ b/src/collections/domain/dtos/CollectionFeaturedItemsDTO.ts @@ -1,9 +1,19 @@ -export type CollectionFeaturedItemsDTO = CollectionFeaturedItemDTO[] +import { CustomFeaturedItem, DvObjectFeaturedItem } from '../models/CollectionFeaturedItem' -export interface CollectionFeaturedItemDTO { +export type CollectionFeaturedItemsDTO = (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 index 88a3777d..0479b491 100644 --- a/src/collections/domain/models/CollectionFeaturedItem.ts +++ b/src/collections/domain/models/CollectionFeaturedItem.ts @@ -1,7 +1,17 @@ -export interface CollectionFeaturedItem { +export type CollectionFeaturedItem = CustomFeaturedItem | DvObjectFeaturedItem + +export interface CustomFeaturedItem { id: number + type: 'custom' content: string imageFileName?: string imageFileUrl?: string displayOrder: number } + +export interface DvObjectFeaturedItem { + id: number + type: 'collection' | 'dataset' | 'file' + dvObjectIdentifier: string + displayOrder: number +} diff --git a/src/collections/infra/repositories/CollectionsRepository.ts b/src/collections/infra/repositories/CollectionsRepository.ts index 2be641b0..4171b646 100644 --- a/src/collections/infra/repositories/CollectionsRepository.ts +++ b/src/collections/infra/repositories/CollectionsRepository.ts @@ -20,7 +20,11 @@ import { import { CollectionItemType } from '../../domain/models/CollectionItemType' import { CollectionFeaturedItem } from '../../domain/models/CollectionFeaturedItem' import { transformCollectionFeaturedItemsPayloadToCollectionFeaturedItems } from './transformers/collectionFeaturedItemsTransformer' -import { CollectionFeaturedItemsDTO } from '../../domain/dtos/CollectionFeaturedItemsDTO' +import { + CollectionFeaturedItemsDTO, + CustomFeaturedItemDTO, + DvObjectFeaturedItemDTO +} from '../../domain/dtos/CollectionFeaturedItemsDTO' import { ApiConstants } from '../../../core/infra/repositories/ApiConstants' import { PublicationStatus } from '../../../../src/core/domain/models/PublicationStatus' import { ReadError } from '../../../core/domain/repositories/ReadError' @@ -382,18 +386,40 @@ export class CollectionsRepository extends ApiRepository implements ICollections 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('type', item.type) + formData.append('displayOrder', item.displayOrder.toString()) + + if (item.type === 'custom') { + // CustomFeaturedItemDTO + 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) + } + } else { + // DvObjectFeaturedItemDTO + 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', '') } + + // 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) + // } }) return formData diff --git a/src/collections/infra/repositories/transformers/CollectionFeaturedItemPayload.ts b/src/collections/infra/repositories/transformers/CollectionFeaturedItemPayload.ts index 34ba0dcf..d3dec1fe 100644 --- a/src/collections/infra/repositories/transformers/CollectionFeaturedItemPayload.ts +++ b/src/collections/infra/repositories/transformers/CollectionFeaturedItemPayload.ts @@ -1,7 +1,17 @@ -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 + displayOrder: number +} diff --git a/src/collections/infra/repositories/transformers/collectionFeaturedItemsTransformer.ts b/src/collections/infra/repositories/transformers/collectionFeaturedItemsTransformer.ts index f3bfb135..7ef78fec 100644 --- a/src/collections/infra/repositories/transformers/collectionFeaturedItemsTransformer.ts +++ b/src/collections/infra/repositories/transformers/collectionFeaturedItemsTransformer.ts @@ -1,16 +1,52 @@ -import { CollectionFeaturedItem } from '../../../domain/models/CollectionFeaturedItem' -import { CollectionFeaturedItemPayload } from './CollectionFeaturedItemPayload' +import { + CollectionFeaturedItem, + CustomFeaturedItem, + DvObjectFeaturedItem +} from '../../../domain/models/CollectionFeaturedItem' +import { + CollectionFeaturedItemPayload, + DvObjectFeaturedItemPayload +} from './CollectionFeaturedItemPayload' + +const dvObjectTypeMap: Record = { + dataverse: 'collection', + dataset: 'dataset', + datafile: 'file' +} export const transformCollectionFeaturedItemsPayloadToCollectionFeaturedItems = ( - collectionFeaturedItemsPayload: CollectionFeaturedItemPayload[] + payload: CollectionFeaturedItemPayload[] ): CollectionFeaturedItem[] => { - return collectionFeaturedItemsPayload - .map((collectionFeaturedItemPayload) => ({ - id: collectionFeaturedItemPayload.id, - content: collectionFeaturedItemPayload.content, - imageFileUrl: collectionFeaturedItemPayload.imageFileUrl || undefined, - imageFileName: collectionFeaturedItemPayload.imageFileName || undefined, - displayOrder: collectionFeaturedItemPayload.displayOrder - })) + return payload + .map((item) => { + if (item.type === 'custom') { + const customFeaturedItem: CustomFeaturedItem = { + id: item.id, + type: '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 = dvObjectTypeMap[item.type] + + if (!type) { + throw new Error(`Unknown dvObject type: ${item.type}`) + } + + const dvObjectFeaturedItem: DvObjectFeaturedItem = { + id: item.id, + type, + dvObjectIdentifier: item.dvObjectIdentifier, + displayOrder: item.displayOrder + } + + return dvObjectFeaturedItem + } + }) .sort((a, b) => a.displayOrder - b.displayOrder) } 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..a03af355 100644 --- a/test/functional/collections/GetCollectionFeaturedItems.test.ts +++ b/test/functional/collections/GetCollectionFeaturedItems.test.ts @@ -2,7 +2,7 @@ import { ApiConfig, ReadError, getCollectionFeaturedItems } from '../../../src' import { TestConstants } from '../../testHelpers/TestConstants' import { DataverseApiAuthMechanism } from '../../../src/core/infra/repositories/ApiConfig' import { - createCollectionFeaturedItemViaApi, + createCollectionCustomFeaturedItemViaApi, deleteCollectionFeaturedItemViaApi } from '../../testHelpers/collections/collectionFeaturedItemsHelper' import { @@ -10,6 +10,7 @@ import { deleteCollectionViaApi } from '../../testHelpers/collections/collectionHelper' import { ROOT_COLLECTION_ID } from '../../../src/collections/domain/models/Collection' +import { CustomFeaturedItem } from '../../../src/collections/domain/models/CollectionFeaturedItem' describe('execute', () => { const testCollectionAlias = 'getCollectionsFeaturedItemsTest' @@ -27,12 +28,15 @@ describe('execute', () => { try { await createCollectionViaApi(testCollectionAlias) - const featuredItemCreated = await createCollectionFeaturedItemViaApi(testCollectionAlias, { - content: '

Test content

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

Test content

', + displayOrder: 1, + withFile: true, + fileName: 'featured-item-test-image.png' + } + ) testFeaturedItemId = featuredItemCreated.id } catch (error) { @@ -54,30 +58,37 @@ describe('execute', () => { test('should return featured items array given a valid collection alias that has featured items', async () => { const featuredItemsResponse = await getCollectionFeaturedItems.execute(testCollectionAlias) + const featuredItemOne = featuredItemsResponse[0] as CustomFeaturedItem + 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}` + expect(featuredItemOne.id).toBe(testFeaturedItemId) + expect(featuredItemOne.displayOrder).toBe(1) + expect(featuredItemOne.content).toBe('

Test content

') + expect(featuredItemOne.imageFileUrl).toBe( + `http://localhost:8080/api/access/dataverseFeaturedItemImage/${featuredItemOne.id}` ) - expect(featuredItemsResponse[0].imageFileName).toBe('featured-item-test-image.png') + expect(featuredItemOne.imageFileName).toBe('featured-item-test-image.png') }) 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: 2 + } + ) const featuredItemsResponse = await getCollectionFeaturedItems.execute(testCollectionAlias) + const featuredItemTwo = featuredItemsResponse[1] as CustomFeaturedItem + 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() + expect(featuredItemTwo.id).toBe(featuredItemCreated.id) + expect(featuredItemTwo.displayOrder).toBe(2) + 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..5f89f4b9 100644 --- a/test/functional/collections/UpdateCollectionFeaturedItems.test.ts +++ b/test/functional/collections/UpdateCollectionFeaturedItems.test.ts @@ -7,7 +7,7 @@ import { import { TestConstants } from '../../testHelpers/TestConstants' import { DataverseApiAuthMechanism } from '../../../src/core/infra/repositories/ApiConfig' import { - createCollectionFeaturedItemViaApi, + createCollectionCustomFeaturedItemViaApi, createImageFile, deleteCollectionFeaturedItemViaApi } from '../../testHelpers/collections/collectionFeaturedItemsHelper' @@ -17,6 +17,7 @@ import { deleteCollectionViaApi, EXPECTED_CONTENT_FIELD_WITH_ALL_TAGS } from '../../testHelpers/collections/collectionHelper' +import { CustomFeaturedItem } from '../../../src/collections/domain/models/CollectionFeaturedItem' describe('execute', () => { const testCollectionAlias = 'updateCollectionFeaturedItemsTest' @@ -52,18 +53,21 @@ describe('execute', () => { it('should successfully update the featured items of a collection', async () => { const newFeaturedItems: CollectionFeaturedItemsDTO = [ { + type: 'custom', content: '

Test content 1

', displayOrder: 0, file: undefined, keepFile: false }, { + type: 'custom', content: '

Test content 2

', displayOrder: 1, file: undefined, keepFile: false }, { + type: 'custom', content: CONTENT_FIELD_WITH_ALL_TAGS, displayOrder: 2, file: createImageFile('featured-item-test-image-3.png'), @@ -76,22 +80,26 @@ describe('execute', () => { newFeaturedItems ) + const firstItemResponse = updatedFeaturedItemsResponse[0] as CustomFeaturedItem + const secondItemResponse = updatedFeaturedItemsResponse[1] as CustomFeaturedItem + const thirdItemResponse = updatedFeaturedItemsResponse[2] as CustomFeaturedItem + 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() + 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(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(secondItemResponse.content).toBe((newFeaturedItems[1] as CustomFeaturedItem).content) + expect(secondItemResponse.displayOrder).toBe(newFeaturedItems[1].displayOrder) + expect(secondItemResponse.imageFileUrl).toBeUndefined() + expect(secondItemResponse.imageFileName).toBeUndefined() - 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( + 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).toBe( `http://localhost:8080/api/access/dataverseFeaturedItemImage/${updatedFeaturedItemsResponse[2].id}` ) }) @@ -99,18 +107,21 @@ describe('execute', () => { test('should throw an error when collection does not exist', async () => { const newFeaturedItems: CollectionFeaturedItemsDTO = [ { + type: 'custom', content: '

Test content 1

', displayOrder: 0, file: undefined, keepFile: false }, { + type: 'custom', content: '

Test content 2

', displayOrder: 1, file: undefined, keepFile: false }, { + type: 'custom', content: '

Test content 3

', displayOrder: 2, file: createImageFile('featured-item-test-image-3.png'), @@ -135,6 +146,7 @@ describe('execute', () => { test('should throw an error when featured item content is empty', async () => { const newFeaturedItems: CollectionFeaturedItemsDTO = [ { + type: 'custom', content: '', displayOrder: 0, file: undefined, @@ -163,12 +175,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) { @@ -190,6 +205,7 @@ describe('execute', () => { const newFeaturedItems: CollectionFeaturedItemsDTO = [ { id: testFeaturedItemId, + type: 'custom', content: '

Test content Updated

', displayOrder: 0, file: undefined, @@ -204,12 +220,14 @@ 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).toBe( + `http://localhost:8080/api/access/dataverseFeaturedItemImage/${updatedFeaturedItem.id}` ) }) @@ -217,6 +235,7 @@ describe('execute', () => { const newFeaturedItems: CollectionFeaturedItemsDTO = [ { id: testFeaturedItemId, + type: 'custom', content: '

Test content Updated

', displayOrder: 0, file: undefined, @@ -231,16 +250,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 = [ { id: testFeaturedItemId, + type: 'custom', content: '

Test content Updated

', displayOrder: 0, file: createImageFile('featured-item-test-image-updated.png'), @@ -255,13 +277,13 @@ 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).toBe( + `http://localhost:8080/api/access/dataverseFeaturedItemImage/${updatedFeaturedItem.id}` ) }) @@ -269,6 +291,7 @@ describe('execute', () => { const newFeaturedItems: CollectionFeaturedItemsDTO = [ { id: testFeaturedItemId, + type: 'custom', content: '

Test content Updated

', displayOrder: 0, file: createImageFile('featured-item-test-image-updated.png'), @@ -281,13 +304,15 @@ 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( + expect(testFeaturedItem.imageFileName).toEqual(testFeaturedItemFilename) + expect(testFeaturedItem.imageFileUrl).toBe( `http://localhost:8080/api/access/dataverseFeaturedItemImage/${updatedFeaturedItemsResponse[0].id}` ) }) diff --git a/test/integration/collections/CollectionsRepository.test.ts b/test/integration/collections/CollectionsRepository.test.ts index e759e106..f2e8ac69 100644 --- a/test/integration/collections/CollectionsRepository.test.ts +++ b/test/integration/collections/CollectionsRepository.test.ts @@ -38,12 +38,13 @@ import { } from '../../../src/collections/domain/models/CollectionSearchCriteria' import { ROOT_COLLECTION_ID } from '../../../src/collections/domain/models/Collection' import { - createCollectionFeaturedItemViaApi, + createCollectionCustomFeaturedItemViaApi, createImageFile, deleteCollectionFeaturedItemsViaApi, deleteCollectionFeaturedItemViaApi } from '../../testHelpers/collections/collectionFeaturedItemsHelper' import { createApiTokenViaApi } from '../../testHelpers/users/apiTokenHelper' +import { CustomFeaturedItem } from '../../../src/collections/domain/models/CollectionFeaturedItem' describe('CollectionsRepository', () => { const testCollectionAlias = 'collectionsRepositoryTestCollection' @@ -1162,12 +1163,15 @@ describe('CollectionsRepository', () => { beforeAll(async () => { try { - const featuredItemCreated = await createCollectionFeaturedItemViaApi(testCollectionAlias, { - content: '

Test content

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

Test content

', + displayOrder: 1, + withFile: true, + fileName: 'featured-item-test-image.png' + } + ) testFeaturedItemId = featuredItemCreated.id } catch (error) { @@ -1195,13 +1199,14 @@ describe('CollectionsRepository', () => { 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(testFeaturedItemId) + expect(firstFeaturedItem.displayOrder).toBe(1) + expect(firstFeaturedItem.content).toBe('

Test content

') + expect(firstFeaturedItem.imageFileUrl).toBe( + `http://localhost:8080/api/access/dataverseFeaturedItemImage/${firstFeaturedItem.id}` ) - expect(featuredItemsResponse[0].imageFileName).toBe('featured-item-test-image.png') + expect(firstFeaturedItem.imageFileName).toBe('featured-item-test-image.png') }) test('should return error when collection does not exist', async () => { @@ -1214,8 +1219,13 @@ describe('CollectionsRepository', () => { expectedError ) }) - }) + // Should return error when the dvObjectIdentifier type 'collection' doesnt exist + // Should return error when the dvObjectIdentifier type 'dataset' doesnt exist + // Should return error when the dvObjectIdentifier type 'file' doesnt exist + // Should not return any more dv objects items dataset or file that were deaccessioned or restricted + }) + // eslint-disable-next-line no-unused-vars describe('updateCollectionFeaturedItems', () => { afterAll(async () => { try { @@ -1230,18 +1240,21 @@ describe('CollectionsRepository', () => { it('should update collection featured items sending all new items', async () => { const newFeaturedItems: CollectionFeaturedItemsDTO = [ { + type: 'custom', content: '

Test content 1

', displayOrder: 0, file: undefined, keepFile: false }, { + type: 'custom', content: '

Test content 2

', displayOrder: 1, file: undefined, keepFile: false }, { + type: 'custom', content: '

Test content 3

', displayOrder: 2, file: createImageFile('featured-item-test-image-3.png'), @@ -1256,40 +1269,40 @@ 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) - - 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) - - 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}` - ) + // 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) + + // 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) + + // 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}` + // ) }) }) 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 diff --git a/test/testHelpers/collections/collectionFeaturedItemsHelper.ts b/test/testHelpers/collections/collectionFeaturedItemsHelper.ts index 48032389..5d23bf1d 100644 --- a/test/testHelpers/collections/collectionFeaturedItemsHelper.ts +++ b/test/testHelpers/collections/collectionFeaturedItemsHelper.ts @@ -12,7 +12,7 @@ interface CreateCollectionFeaturedItemData { fileName?: string } -export async function createCollectionFeaturedItemViaApi( +export async function createCollectionCustomFeaturedItemViaApi( collectionAlias: string, { content, @@ -27,6 +27,7 @@ export async function createCollectionFeaturedItemViaApi( } const formData = new FormData() + formData.append('type', 'custom') formData.append('content', content) formData.append('displayOrder', displayOrder.toString()) @@ -83,6 +84,7 @@ export const createCollectionFeaturedItemsModel = (): CollectionFeaturedItem[] = return [ { id: 1, + type: 'custom', content: 'This is a featured item', displayOrder: 1, imageFileName: 'test-image.png', @@ -90,10 +92,17 @@ export const createCollectionFeaturedItemsModel = (): CollectionFeaturedItem[] = }, { id: 2, + type: 'custom', content: 'This is another featured item', displayOrder: 2, imageFileName: undefined, imageFileUrl: undefined + }, + { + id: 3, + type: 'dataset', + displayOrder: 3, + dvObjectIdentifier: 'doi:10.5072/FK2/8YOKQI' } ] } @@ -102,6 +111,7 @@ export const createCollectionFeaturedItemsDTO = (): CollectionFeaturedItemsDTO = return [ { id: 1, + type: 'custom', content: 'This is a featured item', displayOrder: 1, file: createImageFile(), @@ -109,10 +119,29 @@ export const createCollectionFeaturedItemsDTO = (): CollectionFeaturedItemsDTO = }, { id: 2, + type: 'custom', content: 'This is another featured item', displayOrder: 2, file: undefined, keepFile: false + }, + { + id: 3, + type: 'collection', + displayOrder: 3, + dvObjectIdentifier: 'collection-alias-foo-bar' + }, + { + id: 4, + type: 'dataset', + displayOrder: 4, + dvObjectIdentifier: 'doi:10.5072/FK2/8YOKQI' + }, + { + id: 5, + type: 'file', + displayOrder: 5, + dvObjectIdentifier: '12' } ] } From 8f2172fa35d3e0898ce91b17f8efef26e7f6706a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Germ=C3=A1n=20Saracca?= Date: Mon, 9 Jun 2025 14:35:22 -0300 Subject: [PATCH 02/16] feat: update code to work with dv objects --- .../domain/models/CollectionFeaturedItem.ts | 8 +++++- .../repositories/CollectionsRepository.ts | 28 +++++++++---------- .../collectionFeaturedItemsTransformer.ts | 27 +++++++++++++----- 3 files changed, 40 insertions(+), 23 deletions(-) diff --git a/src/collections/domain/models/CollectionFeaturedItem.ts b/src/collections/domain/models/CollectionFeaturedItem.ts index 0479b491..9cb79635 100644 --- a/src/collections/domain/models/CollectionFeaturedItem.ts +++ b/src/collections/domain/models/CollectionFeaturedItem.ts @@ -11,7 +11,13 @@ export interface CustomFeaturedItem { export interface DvObjectFeaturedItem { id: number - type: 'collection' | 'dataset' | 'file' + type: DvObjectFeaturedItemType dvObjectIdentifier: string displayOrder: number } + +export enum DvObjectFeaturedItemType { + COLLECTION = 'collection', + DATASET = 'dataset', + FILE = 'file' +} diff --git a/src/collections/infra/repositories/CollectionsRepository.ts b/src/collections/infra/repositories/CollectionsRepository.ts index 4171b646..734a84ab 100644 --- a/src/collections/infra/repositories/CollectionsRepository.ts +++ b/src/collections/infra/repositories/CollectionsRepository.ts @@ -18,8 +18,14 @@ import { SortType } from '../../domain/models/CollectionSearchCriteria' import { CollectionItemType } from '../../domain/models/CollectionItemType' -import { CollectionFeaturedItem } from '../../domain/models/CollectionFeaturedItem' -import { transformCollectionFeaturedItemsPayloadToCollectionFeaturedItems } from './transformers/collectionFeaturedItemsTransformer' +import { + CollectionFeaturedItem, + DvObjectFeaturedItem +} from '../../domain/models/CollectionFeaturedItem' +import { + domainTypeToApiType, + transformCollectionFeaturedItemsPayloadToCollectionFeaturedItems +} from './transformers/collectionFeaturedItemsTransformer' import { CollectionFeaturedItemsDTO, CustomFeaturedItemDTO, @@ -388,19 +394,23 @@ export class CollectionsRepository extends ApiRepository implements ICollections orderedFeaturedItemsDTO.forEach((item: CustomFeaturedItemDTO | DvObjectFeaturedItemDTO) => { formData.append('id', item.id !== undefined ? item.id.toString() : '0') - formData.append('type', item.type) formData.append('displayOrder', item.displayOrder.toString()) if (item.type === '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 @@ -408,18 +418,6 @@ export class CollectionsRepository extends ApiRepository implements ICollections formData.append('keepFile', '') formData.append('fileName', '') } - - // 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) - // } }) return formData diff --git a/src/collections/infra/repositories/transformers/collectionFeaturedItemsTransformer.ts b/src/collections/infra/repositories/transformers/collectionFeaturedItemsTransformer.ts index 7ef78fec..0738e48d 100644 --- a/src/collections/infra/repositories/transformers/collectionFeaturedItemsTransformer.ts +++ b/src/collections/infra/repositories/transformers/collectionFeaturedItemsTransformer.ts @@ -1,17 +1,30 @@ import { CollectionFeaturedItem, CustomFeaturedItem, - DvObjectFeaturedItem + DvObjectFeaturedItem, + DvObjectFeaturedItemType } from '../../../domain/models/CollectionFeaturedItem' import { CollectionFeaturedItemPayload, DvObjectFeaturedItemPayload } from './CollectionFeaturedItemPayload' -const dvObjectTypeMap: Record = { - dataverse: 'collection', - dataset: 'dataset', - datafile: 'file' +const apiTypeToDomainType: Record< + DvObjectFeaturedItemPayload['type'], + DvObjectFeaturedItem['type'] +> = { + dataverse: DvObjectFeaturedItemType.COLLECTION, + dataset: DvObjectFeaturedItemType.DATASET, + datafile: DvObjectFeaturedItemType.FILE +} + +export const domainTypeToApiType: Record< + DvObjectFeaturedItem['type'], + DvObjectFeaturedItemPayload['type'] +> = { + [DvObjectFeaturedItemType.COLLECTION]: 'dataverse', + [DvObjectFeaturedItemType.DATASET]: 'dataset', + [DvObjectFeaturedItemType.FILE]: 'datafile' } export const transformCollectionFeaturedItemsPayloadToCollectionFeaturedItems = ( @@ -32,10 +45,10 @@ export const transformCollectionFeaturedItemsPayloadToCollectionFeaturedItems = return customFeaturedItem } else { // Map API types to domain types - const type = dvObjectTypeMap[item.type] + const type = apiTypeToDomainType[item.type] if (!type) { - throw new Error(`Unknown dvObject type: ${item.type}`) + throw new Error(`Unknown type: ${item.type}`) } const dvObjectFeaturedItem: DvObjectFeaturedItem = { From 4510be5bd582b3dce9f10d7da4ae394d634e419e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Germ=C3=A1n=20Saracca?= Date: Mon, 9 Jun 2025 14:35:52 -0300 Subject: [PATCH 03/16] test: functional cases --- test/environment/.env | 4 +- .../GetCollectionFeaturedItems.test.ts | 52 +++++++-- .../UpdateCollectionFeaturedItems.test.ts | 105 ++++++++++++++++-- .../collectionFeaturedItemsHelper.ts | 54 +++++++-- 4 files changed, 186 insertions(+), 29 deletions(-) diff --git a/test/environment/.env b/test/environment/.env index 0c691d9b..bbffffba 100644 --- a/test/environment/.env +++ b/test/environment/.env @@ -1,6 +1,6 @@ POSTGRES_VERSION=13 DATAVERSE_DB_USER=dataverse SOLR_VERSION=9.8.0 -DATAVERSE_IMAGE_REGISTRY=docker.io -DATAVERSE_IMAGE_TAG=unstable +DATAVERSE_IMAGE_REGISTRY=ghcr.io +DATAVERSE_IMAGE_TAG=11550-update-dv-object-featured-items-response DATAVERSE_BOOTSTRAP_TIMEOUT=5m diff --git a/test/functional/collections/GetCollectionFeaturedItems.test.ts b/test/functional/collections/GetCollectionFeaturedItems.test.ts index a03af355..bf94380e 100644 --- a/test/functional/collections/GetCollectionFeaturedItems.test.ts +++ b/test/functional/collections/GetCollectionFeaturedItems.test.ts @@ -3,18 +3,26 @@ import { TestConstants } from '../../testHelpers/TestConstants' import { DataverseApiAuthMechanism } from '../../../src/core/infra/repositories/ApiConfig' import { 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 } from '../../../src/collections/domain/models/CollectionFeaturedItem' +import { + CustomFeaturedItem, + DvObjectFeaturedItem, + DvObjectFeaturedItemType +} from '../../../src/collections/domain/models/CollectionFeaturedItem' describe('execute', () => { const testCollectionAlias = 'getCollectionsFeaturedItemsTest' - let testFeaturedItemId: number + const featuredCollectionAlias = 'featured-collection-test' + let testFeaturedItemIds: number[] = [] beforeEach(async () => { ApiConfig.init( @@ -28,6 +36,12 @@ describe('execute', () => { 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) + const featuredItemCreated = await createCollectionCustomFeaturedItemViaApi( testCollectionAlias, { @@ -38,19 +52,29 @@ describe('execute', () => { } ) - testFeaturedItemId = featuredItemCreated.id + 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}` ) } }) @@ -59,15 +83,21 @@ describe('execute', () => { const featuredItemsResponse = await getCollectionFeaturedItems.execute(testCollectionAlias) const featuredItemOne = featuredItemsResponse[0] as CustomFeaturedItem + const featuredItemTwo = featuredItemsResponse[1] as DvObjectFeaturedItem - expect(featuredItemsResponse.length).toBe(1) - expect(featuredItemOne.id).toBe(testFeaturedItemId) + 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).toBe( `http://localhost:8080/api/access/dataverseFeaturedItemImage/${featuredItemOne.id}` ) expect(featuredItemOne.imageFileName).toBe('featured-item-test-image.png') + + expect(featuredItemTwo.id).toBe(testFeaturedItemIds[1]) + expect(featuredItemTwo.type).toBe(DvObjectFeaturedItemType.COLLECTION) + expect(featuredItemTwo.dvObjectIdentifier).toBe(featuredCollectionAlias) + expect(featuredItemTwo.displayOrder).toBe(2) }) it('should return imageFileUrl and imageFileName as undefined when featured item does not have an image', async () => { @@ -75,17 +105,17 @@ describe('execute', () => { testCollectionAlias, { content: '

Test content

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

Test content

') expect(featuredItemTwo.imageFileUrl).toBeUndefined() expect(featuredItemTwo.imageFileName).toBeUndefined() diff --git a/test/functional/collections/UpdateCollectionFeaturedItems.test.ts b/test/functional/collections/UpdateCollectionFeaturedItems.test.ts index 5f89f4b9..eed9b9f2 100644 --- a/test/functional/collections/UpdateCollectionFeaturedItems.test.ts +++ b/test/functional/collections/UpdateCollectionFeaturedItems.test.ts @@ -1,6 +1,9 @@ import { ApiConfig, CollectionFeaturedItemsDTO, + createDataset, + CreatedDatasetIdentifiers, + getDatasetFiles, updateCollectionFeaturedItems, WriteError } from '../../../src' @@ -15,12 +18,29 @@ 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 } from '../../../src/collections/domain/models/CollectionFeaturedItem' +import { + CustomFeaturedItem, + DvObjectFeaturedItem, + DvObjectFeaturedItemType + // DvObjectFeaturedItemType +} from '../../../src/collections/domain/models/CollectionFeaturedItem' +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( @@ -31,22 +51,55 @@ 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}` - ) + console.log(error) + 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') } }) @@ -72,6 +125,21 @@ describe('execute', () => { displayOrder: 2, file: createImageFile('featured-item-test-image-3.png'), keepFile: false + }, + { + type: DvObjectFeaturedItemType.COLLECTION, + dvObjectIdentifier: featuredCollectionAlias, + displayOrder: 3 + }, + { + type: DvObjectFeaturedItemType.DATASET, + dvObjectIdentifier: testDatasetIds.persistentId, + displayOrder: 4 + }, + { + type: DvObjectFeaturedItemType.FILE, + dvObjectIdentifier: featuredFileId.toString(), + displayOrder: 5 } ] @@ -83,8 +151,11 @@ describe('execute', () => { 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(3) + expect(updatedFeaturedItemsResponse.length).toBe(6) expect(firstItemResponse.content).toBe((newFeaturedItems[0] as CustomFeaturedItem).content) expect(firstItemResponse.displayOrder).toBe(newFeaturedItems[0].displayOrder) @@ -102,6 +173,21 @@ describe('execute', () => { expect(thirdItemResponse.imageFileUrl).toBe( `http://localhost:8080/api/access/dataverseFeaturedItemImage/${updatedFeaturedItemsResponse[2].id}` ) + + expect(fourthItemResponse.type).toBe(DvObjectFeaturedItemType.COLLECTION) + expect(fourthItemResponse.dvObjectIdentifier).toBe(featuredCollectionAlias) + expect(fourthItemResponse.displayOrder).toBe(newFeaturedItems[3].displayOrder) + expect(fourthItemResponse.id).toBeDefined() + + expect(fifthItemResponse.type).toBe(DvObjectFeaturedItemType.DATASET) + expect(fifthItemResponse.dvObjectIdentifier).toBe(testDatasetIds.persistentId) + expect(fifthItemResponse.displayOrder).toBe(newFeaturedItems[4].displayOrder) + expect(fifthItemResponse.id).toBeDefined() + + expect(sixthItemResponse.type).toBe(DvObjectFeaturedItemType.FILE) + expect(sixthItemResponse.dvObjectIdentifier).toBe(featuredFileId.toString()) + expect(sixthItemResponse.displayOrder).toBe(newFeaturedItems[5].displayOrder) + expect(sixthItemResponse.id).toBeDefined() }) test('should throw an error when collection does not exist', async () => { @@ -187,6 +273,7 @@ describe('execute', () => { testFeaturedItemId = featuredItemCreated.id } catch (error) { + console.log(JSON.stringify(error, null, 2)) throw new Error(`Error while creating collection featured item in ${testCollectionAlias}`) } }) diff --git a/test/testHelpers/collections/collectionFeaturedItemsHelper.ts b/test/testHelpers/collections/collectionFeaturedItemsHelper.ts index 5d23bf1d..0e7de9c5 100644 --- a/test/testHelpers/collections/collectionFeaturedItemsHelper.ts +++ b/test/testHelpers/collections/collectionFeaturedItemsHelper.ts @@ -1,11 +1,15 @@ import axios from 'axios' import { File, Blob } from '@web-std/file' -import { CollectionFeaturedItem } from '../../../src/collections/domain/models/CollectionFeaturedItem' +import { + CollectionFeaturedItem, + DvObjectFeaturedItemType +} from '../../../src/collections/domain/models/CollectionFeaturedItem' import { ROOT_COLLECTION_ID } from '../../../src/collections/domain/models/Collection' import { TestConstants } from '../TestConstants' import { CollectionFeaturedItemsDTO } from '../../../src' +import { DvObjectFeaturedItemPayload } from '../../../src/collections/infra/repositories/transformers/CollectionFeaturedItemPayload' -interface CreateCollectionFeaturedItemData { +interface CreateCollectionCustomFeaturedItemData { content: string displayOrder?: number withFile?: boolean @@ -19,7 +23,7 @@ export async function createCollectionCustomFeaturedItemViaApi( displayOrder = 1, withFile = false, fileName = 'test-image.png' - }: CreateCollectionFeaturedItemData + }: CreateCollectionCustomFeaturedItemData ): Promise { try { if (collectionAlias == undefined) { @@ -53,6 +57,42 @@ export async function createCollectionCustomFeaturedItemViaApi( } } +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( @@ -100,7 +140,7 @@ export const createCollectionFeaturedItemsModel = (): CollectionFeaturedItem[] = }, { id: 3, - type: 'dataset', + type: DvObjectFeaturedItemType.DATASET, displayOrder: 3, dvObjectIdentifier: 'doi:10.5072/FK2/8YOKQI' } @@ -127,19 +167,19 @@ export const createCollectionFeaturedItemsDTO = (): CollectionFeaturedItemsDTO = }, { id: 3, - type: 'collection', + type: DvObjectFeaturedItemType.COLLECTION, displayOrder: 3, dvObjectIdentifier: 'collection-alias-foo-bar' }, { id: 4, - type: 'dataset', + type: DvObjectFeaturedItemType.DATASET, displayOrder: 4, dvObjectIdentifier: 'doi:10.5072/FK2/8YOKQI' }, { id: 5, - type: 'file', + type: DvObjectFeaturedItemType.FILE, displayOrder: 5, dvObjectIdentifier: '12' } From cbc63daceafc80aeec9ed080e4ebbc424dd8fb49 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Germ=C3=A1n=20Saracca?= Date: Mon, 9 Jun 2025 17:18:30 -0300 Subject: [PATCH 04/16] test: integration cases --- .../collections/CollectionsRepository.test.ts | 188 +++++++++++++++--- 1 file changed, 163 insertions(+), 25 deletions(-) diff --git a/test/integration/collections/CollectionsRepository.test.ts b/test/integration/collections/CollectionsRepository.test.ts index f2e8ac69..42c60bc5 100644 --- a/test/integration/collections/CollectionsRepository.test.ts +++ b/test/integration/collections/CollectionsRepository.test.ts @@ -13,7 +13,9 @@ import { WriteError, createDataset, getCollection, - createCollection + createCollection, + getDatasetFiles, + restrictFile } from '../../../src' import { ApiConfig } from '../../../src' import { DataverseApiAuthMechanism } from '../../../src/core/infra/repositories/ApiConfig' @@ -28,7 +30,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' @@ -44,7 +47,11 @@ import { deleteCollectionFeaturedItemViaApi } from '../../testHelpers/collections/collectionFeaturedItemsHelper' import { createApiTokenViaApi } from '../../testHelpers/users/apiTokenHelper' -import { CustomFeaturedItem } from '../../../src/collections/domain/models/CollectionFeaturedItem' +import { + CustomFeaturedItem, + DvObjectFeaturedItemType +} from '../../../src/collections/domain/models/CollectionFeaturedItem' +import { DvObjectFeaturedItemDTO } from '../../../src/collections/domain/dtos/CollectionFeaturedItemsDTO' describe('CollectionsRepository', () => { const testCollectionAlias = 'collectionsRepositoryTestCollection' @@ -1219,13 +1226,8 @@ describe('CollectionsRepository', () => { expectedError ) }) - - // Should return error when the dvObjectIdentifier type 'collection' doesnt exist - // Should return error when the dvObjectIdentifier type 'dataset' doesnt exist - // Should return error when the dvObjectIdentifier type 'file' doesnt exist - // Should not return any more dv objects items dataset or file that were deaccessioned or restricted }) - // eslint-disable-next-line no-unused-vars + describe('updateCollectionFeaturedItems', () => { afterAll(async () => { try { @@ -1269,22 +1271,158 @@ 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) - - // 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) - - // 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}` - // ) + 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).toBe( + `http://localhost:8080/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: DvObjectFeaturedItemType.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: DvObjectFeaturedItemType.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) + }) + + // TODO:ME - Fix from API. getting 500 error right now instead of 400 + + // eslint-disable-next-line jest/no-commented-out-tests + // it('should return error when the dvObjectIdentifier of a file does not exist', async () => { + // const invalidFileId = '99' + // const newFeaturedItems: DvObjectFeaturedItemDTO[] = [ + // { + // type: DvObjectFeaturedItemType.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: DvObjectFeaturedItemType.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: DvObjectFeaturedItemType.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 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: DvObjectFeaturedItemType.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) }) }) From 4d41dc5550734a33df095c00b9c22b99a3e27094 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Germ=C3=A1n=20Saracca?= Date: Wed, 11 Jun 2025 15:06:31 -0300 Subject: [PATCH 05/16] feat: add dvObjectDisplayName property --- .../domain/models/CollectionFeaturedItem.ts | 1 + .../CollectionFeaturedItemPayload.ts | 1 + .../collectionFeaturedItemsTransformer.ts | 1 + .../GetCollectionFeaturedItems.test.ts | 1 + .../UpdateCollectionFeaturedItems.test.ts | 5 +++ .../collections/CollectionsRepository.test.ts | 35 +++++++++---------- .../collectionFeaturedItemsHelper.ts | 3 +- 7 files changed, 27 insertions(+), 20 deletions(-) diff --git a/src/collections/domain/models/CollectionFeaturedItem.ts b/src/collections/domain/models/CollectionFeaturedItem.ts index 9cb79635..050e571b 100644 --- a/src/collections/domain/models/CollectionFeaturedItem.ts +++ b/src/collections/domain/models/CollectionFeaturedItem.ts @@ -13,6 +13,7 @@ export interface DvObjectFeaturedItem { id: number type: DvObjectFeaturedItemType dvObjectIdentifier: string + dvObjectDisplayName: string displayOrder: number } diff --git a/src/collections/infra/repositories/transformers/CollectionFeaturedItemPayload.ts b/src/collections/infra/repositories/transformers/CollectionFeaturedItemPayload.ts index d3dec1fe..06025ee4 100644 --- a/src/collections/infra/repositories/transformers/CollectionFeaturedItemPayload.ts +++ b/src/collections/infra/repositories/transformers/CollectionFeaturedItemPayload.ts @@ -13,5 +13,6 @@ 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 index 0738e48d..4672984c 100644 --- a/src/collections/infra/repositories/transformers/collectionFeaturedItemsTransformer.ts +++ b/src/collections/infra/repositories/transformers/collectionFeaturedItemsTransformer.ts @@ -55,6 +55,7 @@ export const transformCollectionFeaturedItemsPayloadToCollectionFeaturedItems = id: item.id, type, dvObjectIdentifier: item.dvObjectIdentifier, + dvObjectDisplayName: item.dvObjectDisplayName, displayOrder: item.displayOrder } diff --git a/test/functional/collections/GetCollectionFeaturedItems.test.ts b/test/functional/collections/GetCollectionFeaturedItems.test.ts index bf94380e..40affc19 100644 --- a/test/functional/collections/GetCollectionFeaturedItems.test.ts +++ b/test/functional/collections/GetCollectionFeaturedItems.test.ts @@ -97,6 +97,7 @@ describe('execute', () => { expect(featuredItemTwo.id).toBe(testFeaturedItemIds[1]) expect(featuredItemTwo.type).toBe(DvObjectFeaturedItemType.COLLECTION) expect(featuredItemTwo.dvObjectIdentifier).toBe(featuredCollectionAlias) + expect(featuredItemTwo.dvObjectDisplayName).toBe('Scientific Research') expect(featuredItemTwo.displayOrder).toBe(2) }) diff --git a/test/functional/collections/UpdateCollectionFeaturedItems.test.ts b/test/functional/collections/UpdateCollectionFeaturedItems.test.ts index eed9b9f2..ba0cef0a 100644 --- a/test/functional/collections/UpdateCollectionFeaturedItems.test.ts +++ b/test/functional/collections/UpdateCollectionFeaturedItems.test.ts @@ -176,16 +176,21 @@ describe('execute', () => { expect(fourthItemResponse.type).toBe(DvObjectFeaturedItemType.COLLECTION) expect(fourthItemResponse.dvObjectIdentifier).toBe(featuredCollectionAlias) + expect(fourthItemResponse.dvObjectDisplayName).toBe('Scientific Research') expect(fourthItemResponse.displayOrder).toBe(newFeaturedItems[3].displayOrder) expect(fourthItemResponse.id).toBeDefined() expect(fifthItemResponse.type).toBe(DvObjectFeaturedItemType.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(DvObjectFeaturedItemType.FILE) expect(sixthItemResponse.dvObjectIdentifier).toBe(featuredFileId.toString()) + expect(sixthItemResponse.dvObjectDisplayName).toBe(testTextFile1Name) expect(sixthItemResponse.displayOrder).toBe(newFeaturedItems[5].displayOrder) expect(sixthItemResponse.id).toBeDefined() }) diff --git a/test/integration/collections/CollectionsRepository.test.ts b/test/integration/collections/CollectionsRepository.test.ts index 34a79210..b44703e9 100644 --- a/test/integration/collections/CollectionsRepository.test.ts +++ b/test/integration/collections/CollectionsRepository.test.ts @@ -1295,25 +1295,22 @@ describe('CollectionsRepository', () => { ).rejects.toThrow(expectedError) }) - // TODO:ME - Fix from API. getting 500 error right now instead of 400 - - // eslint-disable-next-line jest/no-commented-out-tests - // it('should return error when the dvObjectIdentifier of a file does not exist', async () => { - // const invalidFileId = '99' - // const newFeaturedItems: DvObjectFeaturedItemDTO[] = [ - // { - // type: DvObjectFeaturedItemType.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 dvObjectIdentifier of a file does not exist', async () => { + const invalidFileId = '99' + const newFeaturedItems: DvObjectFeaturedItemDTO[] = [ + { + type: DvObjectFeaturedItemType.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' diff --git a/test/testHelpers/collections/collectionFeaturedItemsHelper.ts b/test/testHelpers/collections/collectionFeaturedItemsHelper.ts index 0e7de9c5..1fbca22d 100644 --- a/test/testHelpers/collections/collectionFeaturedItemsHelper.ts +++ b/test/testHelpers/collections/collectionFeaturedItemsHelper.ts @@ -142,7 +142,8 @@ export const createCollectionFeaturedItemsModel = (): CollectionFeaturedItem[] = id: 3, type: DvObjectFeaturedItemType.DATASET, displayOrder: 3, - dvObjectIdentifier: 'doi:10.5072/FK2/8YOKQI' + dvObjectIdentifier: 'doi:10.5072/FK2/8YOKQI', + dvObjectDisplayName: 'Dataset Title' } ] } From be0beb906e18abbcaf47f59af72794567af6badf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Germ=C3=A1n=20Saracca?= Date: Wed, 11 Jun 2025 16:31:58 -0300 Subject: [PATCH 06/16] refactor: use enum for 'custom' also --- .../domain/models/CollectionFeaturedItem.ts | 7 ++-- .../collectionFeaturedItemsTransformer.ts | 16 ++++---- .../GetCollectionFeaturedItems.test.ts | 4 +- .../UpdateCollectionFeaturedItems.test.ts | 37 +++++++++---------- .../collections/CollectionsRepository.test.ts | 20 +++++----- .../collectionFeaturedItemsHelper.ts | 18 ++++----- 6 files changed, 51 insertions(+), 51 deletions(-) diff --git a/src/collections/domain/models/CollectionFeaturedItem.ts b/src/collections/domain/models/CollectionFeaturedItem.ts index 050e571b..6b53e799 100644 --- a/src/collections/domain/models/CollectionFeaturedItem.ts +++ b/src/collections/domain/models/CollectionFeaturedItem.ts @@ -2,7 +2,7 @@ export type CollectionFeaturedItem = CustomFeaturedItem | DvObjectFeaturedItem export interface CustomFeaturedItem { id: number - type: 'custom' + type: FeaturedItemType.CUSTOM content: string imageFileName?: string imageFileUrl?: string @@ -11,13 +11,14 @@ export interface CustomFeaturedItem { export interface DvObjectFeaturedItem { id: number - type: DvObjectFeaturedItemType + type: FeaturedItemType.COLLECTION | FeaturedItemType.DATASET | FeaturedItemType.FILE dvObjectIdentifier: string dvObjectDisplayName: string displayOrder: number } -export enum DvObjectFeaturedItemType { +export enum FeaturedItemType { + CUSTOM = 'custom', COLLECTION = 'collection', DATASET = 'dataset', FILE = 'file' diff --git a/src/collections/infra/repositories/transformers/collectionFeaturedItemsTransformer.ts b/src/collections/infra/repositories/transformers/collectionFeaturedItemsTransformer.ts index 4672984c..a47e0320 100644 --- a/src/collections/infra/repositories/transformers/collectionFeaturedItemsTransformer.ts +++ b/src/collections/infra/repositories/transformers/collectionFeaturedItemsTransformer.ts @@ -2,7 +2,7 @@ import { CollectionFeaturedItem, CustomFeaturedItem, DvObjectFeaturedItem, - DvObjectFeaturedItemType + FeaturedItemType } from '../../../domain/models/CollectionFeaturedItem' import { CollectionFeaturedItemPayload, @@ -13,18 +13,18 @@ const apiTypeToDomainType: Record< DvObjectFeaturedItemPayload['type'], DvObjectFeaturedItem['type'] > = { - dataverse: DvObjectFeaturedItemType.COLLECTION, - dataset: DvObjectFeaturedItemType.DATASET, - datafile: DvObjectFeaturedItemType.FILE + dataverse: FeaturedItemType.COLLECTION, + dataset: FeaturedItemType.DATASET, + datafile: FeaturedItemType.FILE } export const domainTypeToApiType: Record< DvObjectFeaturedItem['type'], DvObjectFeaturedItemPayload['type'] > = { - [DvObjectFeaturedItemType.COLLECTION]: 'dataverse', - [DvObjectFeaturedItemType.DATASET]: 'dataset', - [DvObjectFeaturedItemType.FILE]: 'datafile' + [FeaturedItemType.COLLECTION]: 'dataverse', + [FeaturedItemType.DATASET]: 'dataset', + [FeaturedItemType.FILE]: 'datafile' } export const transformCollectionFeaturedItemsPayloadToCollectionFeaturedItems = ( @@ -35,7 +35,7 @@ export const transformCollectionFeaturedItemsPayloadToCollectionFeaturedItems = if (item.type === 'custom') { const customFeaturedItem: CustomFeaturedItem = { id: item.id, - type: 'custom', + type: FeaturedItemType.CUSTOM, content: item.content, imageFileUrl: item.imageFileUrl || undefined, imageFileName: item.imageFileName || undefined, diff --git a/test/functional/collections/GetCollectionFeaturedItems.test.ts b/test/functional/collections/GetCollectionFeaturedItems.test.ts index 40affc19..2ddcf8df 100644 --- a/test/functional/collections/GetCollectionFeaturedItems.test.ts +++ b/test/functional/collections/GetCollectionFeaturedItems.test.ts @@ -16,7 +16,7 @@ import { ROOT_COLLECTION_ID } from '../../../src/collections/domain/models/Colle import { CustomFeaturedItem, DvObjectFeaturedItem, - DvObjectFeaturedItemType + FeaturedItemType } from '../../../src/collections/domain/models/CollectionFeaturedItem' describe('execute', () => { @@ -95,7 +95,7 @@ describe('execute', () => { expect(featuredItemOne.imageFileName).toBe('featured-item-test-image.png') expect(featuredItemTwo.id).toBe(testFeaturedItemIds[1]) - expect(featuredItemTwo.type).toBe(DvObjectFeaturedItemType.COLLECTION) + expect(featuredItemTwo.type).toBe(FeaturedItemType.COLLECTION) expect(featuredItemTwo.dvObjectIdentifier).toBe(featuredCollectionAlias) expect(featuredItemTwo.dvObjectDisplayName).toBe('Scientific Research') expect(featuredItemTwo.displayOrder).toBe(2) diff --git a/test/functional/collections/UpdateCollectionFeaturedItems.test.ts b/test/functional/collections/UpdateCollectionFeaturedItems.test.ts index ba0cef0a..05919a42 100644 --- a/test/functional/collections/UpdateCollectionFeaturedItems.test.ts +++ b/test/functional/collections/UpdateCollectionFeaturedItems.test.ts @@ -24,8 +24,7 @@ import { import { CustomFeaturedItem, DvObjectFeaturedItem, - DvObjectFeaturedItemType - // DvObjectFeaturedItemType + FeaturedItemType } from '../../../src/collections/domain/models/CollectionFeaturedItem' import { uploadFileViaApi } from '../../testHelpers/files/filesHelper' import { @@ -106,38 +105,38 @@ describe('execute', () => { it('should successfully update the featured items of a collection', async () => { const newFeaturedItems: CollectionFeaturedItemsDTO = [ { - type: 'custom', + type: FeaturedItemType.CUSTOM, content: '

Test content 1

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

Test content 2

', displayOrder: 1, file: undefined, keepFile: false }, { - type: 'custom', + type: FeaturedItemType.CUSTOM, content: CONTENT_FIELD_WITH_ALL_TAGS, displayOrder: 2, file: createImageFile('featured-item-test-image-3.png'), keepFile: false }, { - type: DvObjectFeaturedItemType.COLLECTION, + type: FeaturedItemType.COLLECTION, dvObjectIdentifier: featuredCollectionAlias, displayOrder: 3 }, { - type: DvObjectFeaturedItemType.DATASET, + type: FeaturedItemType.DATASET, dvObjectIdentifier: testDatasetIds.persistentId, displayOrder: 4 }, { - type: DvObjectFeaturedItemType.FILE, + type: FeaturedItemType.FILE, dvObjectIdentifier: featuredFileId.toString(), displayOrder: 5 } @@ -174,13 +173,13 @@ describe('execute', () => { `http://localhost:8080/api/access/dataverseFeaturedItemImage/${updatedFeaturedItemsResponse[2].id}` ) - expect(fourthItemResponse.type).toBe(DvObjectFeaturedItemType.COLLECTION) + 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(fifthItemResponse.type).toBe(DvObjectFeaturedItemType.DATASET) + expect(fifthItemResponse.type).toBe(FeaturedItemType.DATASET) expect(fifthItemResponse.dvObjectIdentifier).toBe(testDatasetIds.persistentId) expect(fifthItemResponse.dvObjectDisplayName).toBe( 'Dataset created using the createDataset use case' @@ -188,7 +187,7 @@ describe('execute', () => { expect(fifthItemResponse.displayOrder).toBe(newFeaturedItems[4].displayOrder) expect(fifthItemResponse.id).toBeDefined() - expect(sixthItemResponse.type).toBe(DvObjectFeaturedItemType.FILE) + expect(sixthItemResponse.type).toBe(FeaturedItemType.FILE) expect(sixthItemResponse.dvObjectIdentifier).toBe(featuredFileId.toString()) expect(sixthItemResponse.dvObjectDisplayName).toBe(testTextFile1Name) expect(sixthItemResponse.displayOrder).toBe(newFeaturedItems[5].displayOrder) @@ -198,21 +197,21 @@ describe('execute', () => { test('should throw an error when collection does not exist', async () => { const newFeaturedItems: CollectionFeaturedItemsDTO = [ { - type: 'custom', + type: FeaturedItemType.CUSTOM, content: '

Test content 1

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

Test content 2

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

Test content 3

', displayOrder: 2, file: createImageFile('featured-item-test-image-3.png'), @@ -237,7 +236,7 @@ describe('execute', () => { test('should throw an error when featured item content is empty', async () => { const newFeaturedItems: CollectionFeaturedItemsDTO = [ { - type: 'custom', + type: FeaturedItemType.CUSTOM, content: '', displayOrder: 0, file: undefined, @@ -297,7 +296,7 @@ describe('execute', () => { const newFeaturedItems: CollectionFeaturedItemsDTO = [ { id: testFeaturedItemId, - type: 'custom', + type: FeaturedItemType.CUSTOM, content: '

Test content Updated

', displayOrder: 0, file: undefined, @@ -327,7 +326,7 @@ describe('execute', () => { const newFeaturedItems: CollectionFeaturedItemsDTO = [ { id: testFeaturedItemId, - type: 'custom', + type: FeaturedItemType.CUSTOM, content: '

Test content Updated

', displayOrder: 0, file: undefined, @@ -354,7 +353,7 @@ describe('execute', () => { const newFeaturedItems: CollectionFeaturedItemsDTO = [ { id: testFeaturedItemId, - type: 'custom', + type: FeaturedItemType.CUSTOM, content: '

Test content Updated

', displayOrder: 0, file: createImageFile('featured-item-test-image-updated.png'), @@ -383,7 +382,7 @@ describe('execute', () => { const newFeaturedItems: CollectionFeaturedItemsDTO = [ { id: testFeaturedItemId, - type: 'custom', + type: FeaturedItemType.CUSTOM, content: '

Test content Updated

', displayOrder: 0, file: createImageFile('featured-item-test-image-updated.png'), diff --git a/test/integration/collections/CollectionsRepository.test.ts b/test/integration/collections/CollectionsRepository.test.ts index b44703e9..19eeeff1 100644 --- a/test/integration/collections/CollectionsRepository.test.ts +++ b/test/integration/collections/CollectionsRepository.test.ts @@ -49,7 +49,7 @@ import { import { createApiTokenViaApi } from '../../testHelpers/users/apiTokenHelper' import { CustomFeaturedItem, - DvObjectFeaturedItemType + FeaturedItemType } from '../../../src/collections/domain/models/CollectionFeaturedItem' import { DvObjectFeaturedItemDTO } from '../../../src/collections/domain/dtos/CollectionFeaturedItemsDTO' @@ -1207,21 +1207,21 @@ describe('CollectionsRepository', () => { it('should update collection featured items sending all new items', async () => { const newFeaturedItems: CollectionFeaturedItemsDTO = [ { - type: 'custom', + type: FeaturedItemType.CUSTOM, content: '

Test content 1

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

Test content 2

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

Test content 3

', displayOrder: 2, file: createImageFile('featured-item-test-image-3.png'), @@ -1264,7 +1264,7 @@ describe('CollectionsRepository', () => { const invalidCollectionAlias = 'invalid-collection-alias' const newFeaturedItems: DvObjectFeaturedItemDTO[] = [ { - type: DvObjectFeaturedItemType.COLLECTION, + type: FeaturedItemType.COLLECTION, dvObjectIdentifier: invalidCollectionAlias, displayOrder: 0 } @@ -1282,7 +1282,7 @@ describe('CollectionsRepository', () => { const invalidDatasetPersistentId = 'doi:10.5072/FK2/INVALID_DATASET' const newFeaturedItems: DvObjectFeaturedItemDTO[] = [ { - type: DvObjectFeaturedItemType.DATASET, + type: FeaturedItemType.DATASET, dvObjectIdentifier: invalidDatasetPersistentId, displayOrder: 0 } @@ -1299,7 +1299,7 @@ describe('CollectionsRepository', () => { const invalidFileId = '99' const newFeaturedItems: DvObjectFeaturedItemDTO[] = [ { - type: DvObjectFeaturedItemType.FILE, + type: FeaturedItemType.FILE, dvObjectIdentifier: invalidFileId, displayOrder: 0 } @@ -1318,7 +1318,7 @@ describe('CollectionsRepository', () => { const newFeaturedItems: DvObjectFeaturedItemDTO[] = [ { - type: DvObjectFeaturedItemType.COLLECTION, + type: FeaturedItemType.COLLECTION, dvObjectIdentifier: unpublishedCollectionAlias, displayOrder: 0 } @@ -1338,7 +1338,7 @@ describe('CollectionsRepository', () => { const newFeaturedItems: DvObjectFeaturedItemDTO[] = [ { - type: DvObjectFeaturedItemType.DATASET, + type: FeaturedItemType.DATASET, dvObjectIdentifier: testDatasetIds.persistentId, displayOrder: 0 } @@ -1372,7 +1372,7 @@ describe('CollectionsRepository', () => { const newFeaturedItems: DvObjectFeaturedItemDTO[] = [ { - type: DvObjectFeaturedItemType.FILE, + type: FeaturedItemType.FILE, dvObjectIdentifier: fileId.toString(), displayOrder: 0 } diff --git a/test/testHelpers/collections/collectionFeaturedItemsHelper.ts b/test/testHelpers/collections/collectionFeaturedItemsHelper.ts index 1fbca22d..7e35bbf7 100644 --- a/test/testHelpers/collections/collectionFeaturedItemsHelper.ts +++ b/test/testHelpers/collections/collectionFeaturedItemsHelper.ts @@ -2,7 +2,7 @@ import axios from 'axios' import { File, Blob } from '@web-std/file' import { CollectionFeaturedItem, - DvObjectFeaturedItemType + FeaturedItemType } from '../../../src/collections/domain/models/CollectionFeaturedItem' import { ROOT_COLLECTION_ID } from '../../../src/collections/domain/models/Collection' import { TestConstants } from '../TestConstants' @@ -124,7 +124,7 @@ export const createCollectionFeaturedItemsModel = (): CollectionFeaturedItem[] = return [ { id: 1, - type: 'custom', + type: FeaturedItemType.CUSTOM, content: 'This is a featured item', displayOrder: 1, imageFileName: 'test-image.png', @@ -132,7 +132,7 @@ export const createCollectionFeaturedItemsModel = (): CollectionFeaturedItem[] = }, { id: 2, - type: 'custom', + type: FeaturedItemType.CUSTOM, content: 'This is another featured item', displayOrder: 2, imageFileName: undefined, @@ -140,7 +140,7 @@ export const createCollectionFeaturedItemsModel = (): CollectionFeaturedItem[] = }, { id: 3, - type: DvObjectFeaturedItemType.DATASET, + type: FeaturedItemType.DATASET, displayOrder: 3, dvObjectIdentifier: 'doi:10.5072/FK2/8YOKQI', dvObjectDisplayName: 'Dataset Title' @@ -152,7 +152,7 @@ export const createCollectionFeaturedItemsDTO = (): CollectionFeaturedItemsDTO = return [ { id: 1, - type: 'custom', + type: FeaturedItemType.CUSTOM, content: 'This is a featured item', displayOrder: 1, file: createImageFile(), @@ -160,7 +160,7 @@ export const createCollectionFeaturedItemsDTO = (): CollectionFeaturedItemsDTO = }, { id: 2, - type: 'custom', + type: FeaturedItemType.CUSTOM, content: 'This is another featured item', displayOrder: 2, file: undefined, @@ -168,19 +168,19 @@ export const createCollectionFeaturedItemsDTO = (): CollectionFeaturedItemsDTO = }, { id: 3, - type: DvObjectFeaturedItemType.COLLECTION, + type: FeaturedItemType.COLLECTION, displayOrder: 3, dvObjectIdentifier: 'collection-alias-foo-bar' }, { id: 4, - type: DvObjectFeaturedItemType.DATASET, + type: FeaturedItemType.DATASET, displayOrder: 4, dvObjectIdentifier: 'doi:10.5072/FK2/8YOKQI' }, { id: 5, - type: DvObjectFeaturedItemType.FILE, + type: FeaturedItemType.FILE, displayOrder: 5, dvObjectIdentifier: '12' } From 95fe0b85e373b0d746940de4b15b6cd06991d877 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Germ=C3=A1n=20Saracca?= Date: Thu, 12 Jun 2025 09:20:22 -0300 Subject: [PATCH 07/16] fix: missing use of enum --- src/collections/infra/repositories/CollectionsRepository.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/collections/infra/repositories/CollectionsRepository.ts b/src/collections/infra/repositories/CollectionsRepository.ts index 45528f8b..ae58b121 100644 --- a/src/collections/infra/repositories/CollectionsRepository.ts +++ b/src/collections/infra/repositories/CollectionsRepository.ts @@ -21,7 +21,8 @@ import { import { CollectionItemType } from '../../domain/models/CollectionItemType' import { CollectionFeaturedItem, - DvObjectFeaturedItem + DvObjectFeaturedItem, + FeaturedItemType } from '../../domain/models/CollectionFeaturedItem' import { domainTypeToApiType, @@ -401,7 +402,7 @@ export class CollectionsRepository extends ApiRepository implements ICollections formData.append('id', item.id !== undefined ? item.id.toString() : '0') formData.append('displayOrder', item.displayOrder.toString()) - if (item.type === 'custom') { + if (item.type === FeaturedItemType.CUSTOM) { // CustomFeaturedItemDTO formData.append('type', item.type) formData.append('content', item.content) From daa3b796668df6d1f72f4a1aecefe5a27138553f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Germ=C3=A1n=20Saracca?= Date: Thu, 12 Jun 2025 11:26:57 -0300 Subject: [PATCH 08/16] feat: add delete single featured item use case --- docs/useCases.md | 21 ++++++ .../repositories/ICollectionsRepository.ts | 1 + .../useCases/DeleteCollectionFeaturedItem.ts | 21 ++++++ src/collections/index.ts | 5 +- .../repositories/CollectionsRepository.ts | 8 +++ .../DeleteCollectionFeaturedItem.test.ts | 70 +++++++++++++++++++ .../collections/CollectionsRepository.test.ts | 36 ++++++++++ .../DeleteCollectionFeaturedItem.test.ts | 29 ++++++++ 8 files changed, 190 insertions(+), 1 deletion(-) create mode 100644 src/collections/domain/useCases/DeleteCollectionFeaturedItem.ts create mode 100644 test/functional/collections/DeleteCollectionFeaturedItem.test.ts create mode 100644 test/unit/collections/DeleteCollectionFeaturedItem.test.ts diff --git a/docs/useCases.md b/docs/useCases.md index edc4f407..d5ae9439 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) @@ -451,6 +452,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/repositories/ICollectionsRepository.ts b/src/collections/domain/repositories/ICollectionsRepository.ts index 6c0baf35..d2c09e4f 100644 --- a/src/collections/domain/repositories/ICollectionsRepository.ts +++ b/src/collections/domain/repositories/ICollectionsRepository.ts @@ -50,4 +50,5 @@ export interface ICollectionsRepository { featuredItemDTOs: CollectionFeaturedItemsDTO ): 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/index.ts b/src/collections/index.ts index 07e79ad3..0b63f573 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' diff --git a/src/collections/infra/repositories/CollectionsRepository.ts b/src/collections/infra/repositories/CollectionsRepository.ts index ae58b121..7cb72bc0 100644 --- a/src/collections/infra/repositories/CollectionsRepository.ts +++ b/src/collections/infra/repositories/CollectionsRepository.ts @@ -436,4 +436,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/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/integration/collections/CollectionsRepository.test.ts b/test/integration/collections/CollectionsRepository.test.ts index 19eeeff1..8b2e0d25 100644 --- a/test/integration/collections/CollectionsRepository.test.ts +++ b/test/integration/collections/CollectionsRepository.test.ts @@ -1440,6 +1440,42 @@ 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/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) + }) +}) From f1b0cfd366d2659adeeaeb8fa86773d7d6aaed9e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Germ=C3=A1n=20Saracca?= Date: Wed, 18 Jun 2025 08:17:45 -0300 Subject: [PATCH 09/16] refactor: remove collection from namings to avoid confusion with featured items type dv object collection --- docs/useCases.md | 14 +++++----- ...eaturedItemsDTO.ts => FeaturedItemsDTO.ts} | 4 +-- ...lectionFeaturedItem.ts => FeaturedItem.ts} | 2 +- .../repositories/ICollectionsRepository.ts | 12 ++++----- .../useCases/GetCollectionFeaturedItems.ts | 10 +++---- .../useCases/UpdateCollectionFeaturedItems.ts | 16 ++++++------ src/collections/index.ts | 4 +-- .../repositories/CollectionsRepository.ts | 26 ++++++++----------- ...sformer.ts => featuredItemsTransformer.ts} | 8 +++--- .../GetCollectionFeaturedItems.test.ts | 2 +- .../UpdateCollectionFeaturedItems.test.ts | 18 ++++++------- .../collections/CollectionsRepository.test.ts | 10 ++++--- .../collectionFeaturedItemsHelper.ts | 15 +++++------ 13 files changed, 66 insertions(+), 75 deletions(-) rename src/collections/domain/dtos/{CollectionFeaturedItemsDTO.ts => FeaturedItemsDTO.ts} (75%) rename src/collections/domain/models/{CollectionFeaturedItem.ts => FeaturedItem.ts} (86%) rename src/collections/infra/repositories/transformers/{collectionFeaturedItemsTransformer.ts => featuredItemsTransformer.ts} (89%) diff --git a/docs/useCases.md b/docs/useCases.md index d5ae9439..ae3aaae0 100644 --- a/docs/useCases.md +++ b/docs/useCases.md @@ -278,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: @@ -289,7 +289,7 @@ const collectionIdOrAlias = 12345 getCollectionFeaturedItems .execute(collectionId) - .then((featuredItems: CollectionFeaturedItem[]) => { + .then((featuredItems: FeaturedItem[]) => { /* ... */ }) .catch((error: Error) => { @@ -406,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: @@ -417,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[]) => { + /* ... */ +}) /* ... */ ``` diff --git a/src/collections/domain/dtos/CollectionFeaturedItemsDTO.ts b/src/collections/domain/dtos/FeaturedItemsDTO.ts similarity index 75% rename from src/collections/domain/dtos/CollectionFeaturedItemsDTO.ts rename to src/collections/domain/dtos/FeaturedItemsDTO.ts index 66e5e54e..434e941a 100644 --- a/src/collections/domain/dtos/CollectionFeaturedItemsDTO.ts +++ b/src/collections/domain/dtos/FeaturedItemsDTO.ts @@ -1,6 +1,6 @@ -import { CustomFeaturedItem, DvObjectFeaturedItem } from '../models/CollectionFeaturedItem' +import { CustomFeaturedItem, DvObjectFeaturedItem } from '../models/FeaturedItem' -export type CollectionFeaturedItemsDTO = (CustomFeaturedItemDTO | DvObjectFeaturedItemDTO)[] +export type FeaturedItemsDTO = (CustomFeaturedItemDTO | DvObjectFeaturedItemDTO)[] export interface CustomFeaturedItemDTO { id?: number diff --git a/src/collections/domain/models/CollectionFeaturedItem.ts b/src/collections/domain/models/FeaturedItem.ts similarity index 86% rename from src/collections/domain/models/CollectionFeaturedItem.ts rename to src/collections/domain/models/FeaturedItem.ts index 6b53e799..99848c72 100644 --- a/src/collections/domain/models/CollectionFeaturedItem.ts +++ b/src/collections/domain/models/FeaturedItem.ts @@ -1,4 +1,4 @@ -export type CollectionFeaturedItem = CustomFeaturedItem | DvObjectFeaturedItem +export type FeaturedItem = CustomFeaturedItem | DvObjectFeaturedItem export interface CustomFeaturedItem { id: number diff --git a/src/collections/domain/repositories/ICollectionsRepository.ts b/src/collections/domain/repositories/ICollectionsRepository.ts index d2c09e4f..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,13 +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/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 0b63f573..c275402e 100644 --- a/src/collections/index.ts +++ b/src/collections/index.ts @@ -51,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 7cb72bc0..dd1c0f1c 100644 --- a/src/collections/infra/repositories/CollectionsRepository.ts +++ b/src/collections/infra/repositories/CollectionsRepository.ts @@ -20,19 +20,19 @@ import { } from '../../domain/models/CollectionSearchCriteria' import { CollectionItemType } from '../../domain/models/CollectionItemType' import { - CollectionFeaturedItem, + FeaturedItem, DvObjectFeaturedItem, FeaturedItemType -} from '../../domain/models/CollectionFeaturedItem' +} from '../../domain/models/FeaturedItem' import { domainTypeToApiType, - transformCollectionFeaturedItemsPayloadToCollectionFeaturedItems + transformFeaturedItemsPayloadToFeaturedItems } from './transformers/collectionFeaturedItemsTransformer' import { - CollectionFeaturedItemsDTO, + FeaturedItemsDTO, CustomFeaturedItemDTO, DvObjectFeaturedItemDTO -} from '../../domain/dtos/CollectionFeaturedItemsDTO' +} 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' @@ -362,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 }) @@ -374,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( @@ -384,15 +382,13 @@ 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) diff --git a/src/collections/infra/repositories/transformers/collectionFeaturedItemsTransformer.ts b/src/collections/infra/repositories/transformers/featuredItemsTransformer.ts similarity index 89% rename from src/collections/infra/repositories/transformers/collectionFeaturedItemsTransformer.ts rename to src/collections/infra/repositories/transformers/featuredItemsTransformer.ts index a47e0320..e295e7ca 100644 --- a/src/collections/infra/repositories/transformers/collectionFeaturedItemsTransformer.ts +++ b/src/collections/infra/repositories/transformers/featuredItemsTransformer.ts @@ -1,9 +1,9 @@ import { - CollectionFeaturedItem, + FeaturedItem, CustomFeaturedItem, DvObjectFeaturedItem, FeaturedItemType -} from '../../../domain/models/CollectionFeaturedItem' +} from '../../../domain/models/FeaturedItem' import { CollectionFeaturedItemPayload, DvObjectFeaturedItemPayload @@ -27,9 +27,9 @@ export const domainTypeToApiType: Record< [FeaturedItemType.FILE]: 'datafile' } -export const transformCollectionFeaturedItemsPayloadToCollectionFeaturedItems = ( +export const transformFeaturedItemsPayloadToFeaturedItems = ( payload: CollectionFeaturedItemPayload[] -): CollectionFeaturedItem[] => { +): FeaturedItem[] => { return payload .map((item) => { if (item.type === 'custom') { diff --git a/test/functional/collections/GetCollectionFeaturedItems.test.ts b/test/functional/collections/GetCollectionFeaturedItems.test.ts index 2ddcf8df..c5d7634b 100644 --- a/test/functional/collections/GetCollectionFeaturedItems.test.ts +++ b/test/functional/collections/GetCollectionFeaturedItems.test.ts @@ -17,7 +17,7 @@ import { CustomFeaturedItem, DvObjectFeaturedItem, FeaturedItemType -} from '../../../src/collections/domain/models/CollectionFeaturedItem' +} from '../../../src/collections/domain/models/FeaturedItem' describe('execute', () => { const testCollectionAlias = 'getCollectionsFeaturedItemsTest' diff --git a/test/functional/collections/UpdateCollectionFeaturedItems.test.ts b/test/functional/collections/UpdateCollectionFeaturedItems.test.ts index 05919a42..007d21d7 100644 --- a/test/functional/collections/UpdateCollectionFeaturedItems.test.ts +++ b/test/functional/collections/UpdateCollectionFeaturedItems.test.ts @@ -1,6 +1,6 @@ import { ApiConfig, - CollectionFeaturedItemsDTO, + FeaturedItemsDTO, createDataset, CreatedDatasetIdentifiers, getDatasetFiles, @@ -25,7 +25,7 @@ import { CustomFeaturedItem, DvObjectFeaturedItem, FeaturedItemType -} from '../../../src/collections/domain/models/CollectionFeaturedItem' +} from '../../../src/collections/domain/models/FeaturedItem' import { uploadFileViaApi } from '../../testHelpers/files/filesHelper' import { deletePublishedDatasetViaApi, @@ -103,7 +103,7 @@ describe('execute', () => { }) it('should successfully update the featured items of a collection', async () => { - const newFeaturedItems: CollectionFeaturedItemsDTO = [ + const newFeaturedItems: FeaturedItemsDTO = [ { type: FeaturedItemType.CUSTOM, content: '

Test content 1

', @@ -195,7 +195,7 @@ describe('execute', () => { }) test('should throw an error when collection does not exist', async () => { - const newFeaturedItems: CollectionFeaturedItemsDTO = [ + const newFeaturedItems: FeaturedItemsDTO = [ { type: FeaturedItemType.CUSTOM, content: '

Test content 1

', @@ -234,7 +234,7 @@ describe('execute', () => { }) test('should throw an error when featured item content is empty', async () => { - const newFeaturedItems: CollectionFeaturedItemsDTO = [ + const newFeaturedItems: FeaturedItemsDTO = [ { type: FeaturedItemType.CUSTOM, content: '', @@ -293,7 +293,7 @@ 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, @@ -323,7 +323,7 @@ describe('execute', () => { }) 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, @@ -350,7 +350,7 @@ describe('execute', () => { }) 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, @@ -379,7 +379,7 @@ describe('execute', () => { }) 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, diff --git a/test/integration/collections/CollectionsRepository.test.ts b/test/integration/collections/CollectionsRepository.test.ts index 8b2e0d25..6911d12d 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, @@ -50,8 +49,11 @@ import { createApiTokenViaApi } from '../../testHelpers/users/apiTokenHelper' import { CustomFeaturedItem, FeaturedItemType -} from '../../../src/collections/domain/models/CollectionFeaturedItem' -import { DvObjectFeaturedItemDTO } from '../../../src/collections/domain/dtos/CollectionFeaturedItemsDTO' +} from '../../../src/collections/domain/models/FeaturedItem' +import { + DvObjectFeaturedItemDTO, + FeaturedItemsDTO +} from '../../../src/collections/domain/dtos/FeaturedItemsDTO' describe('CollectionsRepository', () => { const testCollectionAlias = 'collectionsRepositoryTestCollection' @@ -1205,7 +1207,7 @@ 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

', diff --git a/test/testHelpers/collections/collectionFeaturedItemsHelper.ts b/test/testHelpers/collections/collectionFeaturedItemsHelper.ts index 7e35bbf7..57ef3980 100644 --- a/test/testHelpers/collections/collectionFeaturedItemsHelper.ts +++ b/test/testHelpers/collections/collectionFeaturedItemsHelper.ts @@ -1,12 +1,9 @@ import axios from 'axios' import { File, Blob } from '@web-std/file' -import { - CollectionFeaturedItem, - FeaturedItemType -} 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 CreateCollectionCustomFeaturedItemData { @@ -24,7 +21,7 @@ export async function createCollectionCustomFeaturedItemViaApi( withFile = false, fileName = 'test-image.png' }: CreateCollectionCustomFeaturedItemData -): Promise { +): Promise { try { if (collectionAlias == undefined) { collectionAlias = ROOT_COLLECTION_ID @@ -66,7 +63,7 @@ interface CreateCollectionDvObjectFeaturedItemData { export async function createCollectionDvObjectFeaturedItemViaApi( collectionAlias: string, { type, dvObjectIdentifier, displayOrder = 1 }: CreateCollectionDvObjectFeaturedItemData -): Promise { +): Promise { try { const formData = new FormData() formData.append('type', type) @@ -120,7 +117,7 @@ export async function deleteCollectionFeaturedItemsViaApi(collectionAlias: strin } } -export const createCollectionFeaturedItemsModel = (): CollectionFeaturedItem[] => { +export const createCollectionFeaturedItemsModel = (): FeaturedItem[] => { return [ { id: 1, @@ -148,7 +145,7 @@ export const createCollectionFeaturedItemsModel = (): CollectionFeaturedItem[] = ] } -export const createCollectionFeaturedItemsDTO = (): CollectionFeaturedItemsDTO => { +export const createCollectionFeaturedItemsDTO = (): FeaturedItemsDTO => { return [ { id: 1, From 47e919db7c68b1f497987576556da8aa6bc64f0b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Germ=C3=A1n=20Saracca?= Date: Wed, 18 Jun 2025 08:18:34 -0300 Subject: [PATCH 10/16] refactor: remove collection from namings to avoid confusion with featured items type dv object collection 2 --- src/collections/infra/repositories/CollectionsRepository.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/collections/infra/repositories/CollectionsRepository.ts b/src/collections/infra/repositories/CollectionsRepository.ts index dd1c0f1c..fa0f9872 100644 --- a/src/collections/infra/repositories/CollectionsRepository.ts +++ b/src/collections/infra/repositories/CollectionsRepository.ts @@ -27,7 +27,7 @@ import { import { domainTypeToApiType, transformFeaturedItemsPayloadToFeaturedItems -} from './transformers/collectionFeaturedItemsTransformer' +} from './transformers/featuredItemsTransformer' import { FeaturedItemsDTO, CustomFeaturedItemDTO, From 07d639fb53d48001782d892445cab95709a0dbeb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Germ=C3=A1n=20Saracca?= Date: Wed, 18 Jun 2025 08:26:18 -0300 Subject: [PATCH 11/16] feat: remove logs and spacing --- .../collections/UpdateCollectionFeaturedItems.test.ts | 2 -- test/integration/collections/CollectionsRepository.test.ts | 1 + 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/test/functional/collections/UpdateCollectionFeaturedItems.test.ts b/test/functional/collections/UpdateCollectionFeaturedItems.test.ts index 007d21d7..6bf2e96c 100644 --- a/test/functional/collections/UpdateCollectionFeaturedItems.test.ts +++ b/test/functional/collections/UpdateCollectionFeaturedItems.test.ts @@ -87,7 +87,6 @@ describe('execute', () => { await publishDatasetViaApi(testDatasetIds.numericId) await waitForNoLocks(testDatasetIds.numericId, 10) } catch (error) { - console.log(error) throw new Error('Tests beforeAll(): Error while creating test data') } }) @@ -277,7 +276,6 @@ describe('execute', () => { testFeaturedItemId = featuredItemCreated.id } catch (error) { - console.log(JSON.stringify(error, null, 2)) throw new Error(`Error while creating collection featured item in ${testCollectionAlias}`) } }) diff --git a/test/integration/collections/CollectionsRepository.test.ts b/test/integration/collections/CollectionsRepository.test.ts index 6911d12d..a9498f85 100644 --- a/test/integration/collections/CollectionsRepository.test.ts +++ b/test/integration/collections/CollectionsRepository.test.ts @@ -1478,6 +1478,7 @@ describe('CollectionsRepository', () => { expect(featuredItemsResponseAfterDeletion).toStrictEqual([]) }) }) + describe('getMyDataCollectionItems', () => { let testDatasetIds: CreatedDatasetIdentifiers From 4f24d911bdac297ea437b90e251c7475c40ea7e9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Germ=C3=A1n=20Saracca?= Date: Wed, 18 Jun 2025 08:28:50 -0300 Subject: [PATCH 12/16] test: avoid asserting complete url with origin and port --- .../GetCollectionFeaturedItems.test.ts | 4 ++-- .../UpdateCollectionFeaturedItems.test.ts | 16 ++++++++-------- .../collections/CollectionsRepository.test.ts | 8 ++++---- 3 files changed, 14 insertions(+), 14 deletions(-) diff --git a/test/functional/collections/GetCollectionFeaturedItems.test.ts b/test/functional/collections/GetCollectionFeaturedItems.test.ts index c5d7634b..df7af77e 100644 --- a/test/functional/collections/GetCollectionFeaturedItems.test.ts +++ b/test/functional/collections/GetCollectionFeaturedItems.test.ts @@ -89,8 +89,8 @@ describe('execute', () => { expect(featuredItemOne.id).toBe(testFeaturedItemIds[0]) expect(featuredItemOne.displayOrder).toBe(1) expect(featuredItemOne.content).toBe('

Test content

') - expect(featuredItemOne.imageFileUrl).toBe( - `http://localhost:8080/api/access/dataverseFeaturedItemImage/${featuredItemOne.id}` + expect(featuredItemOne.imageFileUrl).toContain( + `/api/access/dataverseFeaturedItemImage/${featuredItemOne.id}` ) expect(featuredItemOne.imageFileName).toBe('featured-item-test-image.png') diff --git a/test/functional/collections/UpdateCollectionFeaturedItems.test.ts b/test/functional/collections/UpdateCollectionFeaturedItems.test.ts index 6bf2e96c..87d3ebec 100644 --- a/test/functional/collections/UpdateCollectionFeaturedItems.test.ts +++ b/test/functional/collections/UpdateCollectionFeaturedItems.test.ts @@ -168,8 +168,8 @@ describe('execute', () => { 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).toBe( - `http://localhost:8080/api/access/dataverseFeaturedItemImage/${updatedFeaturedItemsResponse[2].id}` + expect(thirdItemResponse.imageFileUrl).toContain( + `/api/access/dataverseFeaturedItemImage/${updatedFeaturedItemsResponse[2].id}` ) expect(fourthItemResponse.type).toBe(FeaturedItemType.COLLECTION) @@ -315,8 +315,8 @@ describe('execute', () => { expect(updatedFeaturedItem.displayOrder).toBe(newFeaturedItems[0].displayOrder) // Should keep the existing file even if a file was not provided because keepFile is true expect(updatedFeaturedItem.imageFileName).toEqual(testFeaturedItemFilename) - expect(updatedFeaturedItem.imageFileUrl).toBe( - `http://localhost:8080/api/access/dataverseFeaturedItemImage/${updatedFeaturedItem.id}` + expect(updatedFeaturedItem.imageFileUrl).toContain( + `/api/access/dataverseFeaturedItemImage/${updatedFeaturedItem.id}` ) }) @@ -371,8 +371,8 @@ describe('execute', () => { 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).toBe( - `http://localhost:8080/api/access/dataverseFeaturedItemImage/${updatedFeaturedItem.id}` + expect(updatedFeaturedItem.imageFileUrl).toContain( + `/api/access/dataverseFeaturedItemImage/${updatedFeaturedItem.id}` ) }) @@ -401,8 +401,8 @@ describe('execute', () => { expect(testFeaturedItem.displayOrder).toBe(newFeaturedItems[0].displayOrder) // Should keep the existing file even if a file was provided because keepFile is true expect(testFeaturedItem.imageFileName).toEqual(testFeaturedItemFilename) - expect(testFeaturedItem.imageFileUrl).toBe( - `http://localhost:8080/api/access/dataverseFeaturedItemImage/${updatedFeaturedItemsResponse[0].id}` + 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 a9498f85..54e9840a 100644 --- a/test/integration/collections/CollectionsRepository.test.ts +++ b/test/integration/collections/CollectionsRepository.test.ts @@ -1177,8 +1177,8 @@ describe('CollectionsRepository', () => { expect(firstFeaturedItem.id).toBe(testFeaturedItemId) expect(firstFeaturedItem.displayOrder).toBe(1) expect(firstFeaturedItem.content).toBe('

Test content

') - expect(firstFeaturedItem.imageFileUrl).toBe( - `http://localhost:8080/api/access/dataverseFeaturedItemImage/${firstFeaturedItem.id}` + expect(firstFeaturedItem.imageFileUrl).toContain( + `/api/access/dataverseFeaturedItemImage/${firstFeaturedItem.id}` ) expect(firstFeaturedItem.imageFileName).toBe('featured-item-test-image.png') }) @@ -1257,8 +1257,8 @@ describe('CollectionsRepository', () => { 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).toBe( - `http://localhost:8080/api/access/dataverseFeaturedItemImage/${response[2].id}` + expect(thirdFeaturedItem.imageFileUrl).toContain( + `/api/access/dataverseFeaturedItemImage/${response[2].id}` ) }) From a57696eb649e7e1ebbaaf39f6891c1bfb22971a4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Germ=C3=A1n=20Saracca?= Date: Thu, 19 Jun 2025 11:39:46 -0300 Subject: [PATCH 13/16] test: add cases about deleting and restricting a file that is featured --- test/environment/.env | 2 +- .../collections/CollectionsRepository.test.ts | 186 +++++++++++++++--- 2 files changed, 155 insertions(+), 33 deletions(-) diff --git a/test/environment/.env b/test/environment/.env index bbffffba..1e6bfffb 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=ghcr.io diff --git a/test/integration/collections/CollectionsRepository.test.ts b/test/integration/collections/CollectionsRepository.test.ts index 54e9840a..d1a2ca74 100644 --- a/test/integration/collections/CollectionsRepository.test.ts +++ b/test/integration/collections/CollectionsRepository.test.ts @@ -14,7 +14,8 @@ import { getCollection, createCollection, getDatasetFiles, - restrictFile + restrictFile, + deleteFile } from '../../../src' import { ApiConfig } from '../../../src' import { DataverseApiAuthMechanism } from '../../../src/core/infra/repositories/ApiConfig' @@ -22,6 +23,7 @@ import { createCollectionDTO, createCollectionViaApi, deleteCollectionViaApi, + publishCollectionViaApi, ROOT_COLLECTION_ALIAS } from '../../testHelpers/collections/collectionHelper' import { CollectionPayload } from '../../../src/collections/infra/repositories/transformers/CollectionPayload' @@ -41,6 +43,7 @@ import { import { ROOT_COLLECTION_ID } from '../../../src/collections/domain/models/Collection' import { createCollectionCustomFeaturedItemViaApi, + createCollectionDvObjectFeaturedItemViaApi, createImageFile, deleteCollectionFeaturedItemsViaApi, deleteCollectionFeaturedItemViaApi @@ -1133,36 +1136,6 @@ describe('CollectionsRepository', () => { }) describe('getCollectionFeaturedItems', () => { - let testFeaturedItemId: number - - beforeAll(async () => { - try { - const featuredItemCreated = await createCollectionCustomFeaturedItemViaApi( - 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) @@ -1170,17 +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) const firstFeaturedItem = featuredItemsResponse[0] as CustomFeaturedItem - expect(firstFeaturedItem.id).toBe(testFeaturedItemId) + 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(firstFeaturedItem.imageFileName).toBe('featured-item-test-image.png') + + await deleteCollectionFeaturedItemViaApi(featuredItemCreated.id) }) test('should return error when collection does not exist', async () => { @@ -1193,6 +1178,114 @@ 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) + }) + + // TODO:ME - Maybe this should be Featured Item type File should not be returned if the file was restricted and the dataset published. Waiting for confirmation + + 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) + + // // 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( + // testFileRestrictedCollectionAlias + // ) + + // expect(featuredItemsResponseAfterDatasetPublish.length).toBe(0) + + await deletePublishedDatasetViaApi(testDatasetIds.persistentId) + await deleteCollectionViaApi(testFileRestrictedCollectionAlias) + }) }) describe('updateCollectionFeaturedItems', () => { @@ -1353,6 +1446,35 @@ describe('CollectionsRepository', () => { 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 + } + ] + + // TODO:ME - Maybe this should be [400] Datafile must be published to be featured. Waiting for confirmation + 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 restricted', async () => { const testDatasetIds = await createDataset.execute( TestConstants.TEST_NEW_DATASET_DTO, From 0208adff57ceca00ce412b7ac6ef3ac918d46450 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Germ=C3=A1n=20Saracca?= Date: Thu, 19 Jun 2025 11:46:15 -0300 Subject: [PATCH 14/16] remove comments --- .../collections/CollectionsRepository.test.ts | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/test/integration/collections/CollectionsRepository.test.ts b/test/integration/collections/CollectionsRepository.test.ts index d1a2ca74..1bfb8920 100644 --- a/test/integration/collections/CollectionsRepository.test.ts +++ b/test/integration/collections/CollectionsRepository.test.ts @@ -1230,8 +1230,6 @@ describe('CollectionsRepository', () => { await deleteCollectionViaApi(testFileDeletedCollectionAlias) }) - // TODO:ME - Maybe this should be Featured Item type File should not be returned if the file was restricted and the dataset published. Waiting for confirmation - test('Featured Item type File should not be returned if the file was restricted', async () => { const testFileRestrictedCollectionAlias = 'testCollectionFeaturedItemsFileRestriction' await createCollectionViaApi(testFileRestrictedCollectionAlias) @@ -1273,16 +1271,6 @@ describe('CollectionsRepository', () => { expect(featuredItemsResponseAfterFileRestriction.length).toBe(0) - // // 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( - // testFileRestrictedCollectionAlias - // ) - - // expect(featuredItemsResponseAfterDatasetPublish.length).toBe(0) - await deletePublishedDatasetViaApi(testDatasetIds.persistentId) await deleteCollectionViaApi(testFileRestrictedCollectionAlias) }) From 36fe79b82c24272f428f0d4304618e43995125bd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Germ=C3=A1n=20Saracca?= Date: Fri, 20 Jun 2025 15:17:59 -0300 Subject: [PATCH 15/16] remove comment --- test/integration/collections/CollectionsRepository.test.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/test/integration/collections/CollectionsRepository.test.ts b/test/integration/collections/CollectionsRepository.test.ts index 1bfb8920..d869834b 100644 --- a/test/integration/collections/CollectionsRepository.test.ts +++ b/test/integration/collections/CollectionsRepository.test.ts @@ -1453,7 +1453,6 @@ describe('CollectionsRepository', () => { } ] - // TODO:ME - Maybe this should be [400] Datafile must be published to be featured. Waiting for confirmation const expectedError = new WriteError('[400] Dataset must be published to be featured.') await expect( From 9b27f8dc00553aed0b03494fc964b9054fd0c284 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Germ=C3=A1n=20Saracca?= Date: Mon, 23 Jun 2025 16:05:17 -0300 Subject: [PATCH 16/16] test: back to unstable image --- test/environment/.env | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/environment/.env b/test/environment/.env index 1e6bfffb..e7b54bde 100644 --- a/test/environment/.env +++ b/test/environment/.env @@ -1,6 +1,6 @@ POSTGRES_VERSION=17 DATAVERSE_DB_USER=dataverse SOLR_VERSION=9.8.0 -DATAVERSE_IMAGE_REGISTRY=ghcr.io -DATAVERSE_IMAGE_TAG=11550-update-dv-object-featured-items-response +DATAVERSE_IMAGE_REGISTRY=docker.io +DATAVERSE_IMAGE_TAG=unstable DATAVERSE_BOOTSTRAP_TIMEOUT=5m