From c61129561b9ce43a4288e3a33b1812c41dd65b8d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Germ=C3=A1n=20Saracca?= Date: Fri, 3 Jan 2025 11:06:01 -0300 Subject: [PATCH 01/18] feat: infra domain and use cases --- .../domain/models/CollectionFeaturedItem.ts | 7 ++++++ .../repositories/ICollectionsRepository.ts | 4 +++ .../useCases/GetCollectionFeaturedItems.ts | 25 +++++++++++++++++++ .../repositories/CollectionsRepository.ts | 14 +++++++++++ .../CollectionFeaturedItemPayload.ts | 7 ++++++ .../collectionFeaturedItemsTransformer.ts | 14 +++++++++++ 6 files changed, 71 insertions(+) create mode 100644 src/collections/domain/models/CollectionFeaturedItem.ts create mode 100644 src/collections/domain/useCases/GetCollectionFeaturedItems.ts create mode 100644 src/collections/infra/repositories/transformers/CollectionFeaturedItemPayload.ts create mode 100644 src/collections/infra/repositories/transformers/collectionFeaturedItemsTransformer.ts diff --git a/src/collections/domain/models/CollectionFeaturedItem.ts b/src/collections/domain/models/CollectionFeaturedItem.ts new file mode 100644 index 00000000..88a3777d --- /dev/null +++ b/src/collections/domain/models/CollectionFeaturedItem.ts @@ -0,0 +1,7 @@ +export interface CollectionFeaturedItem { + id: number + content: string + imageFileName?: string + imageFileUrl?: string + displayOrder: number +} diff --git a/src/collections/domain/repositories/ICollectionsRepository.ts b/src/collections/domain/repositories/ICollectionsRepository.ts index c024779a..91d395aa 100644 --- a/src/collections/domain/repositories/ICollectionsRepository.ts +++ b/src/collections/domain/repositories/ICollectionsRepository.ts @@ -1,6 +1,7 @@ import { CollectionDTO } from '../dtos/CollectionDTO' import { Collection } from '../models/Collection' import { CollectionFacet } from '../models/CollectionFacet' +import { CollectionFeaturedItem } from '../models/CollectionFeaturedItem' import { CollectionItemSubset } from '../models/CollectionItemSubset' import { CollectionSearchCriteria } from '../models/CollectionSearchCriteria' import { CollectionUserPermissions } from '../models/CollectionUserPermissions' @@ -26,4 +27,7 @@ export interface ICollectionsRepository { collectionIdOrAlias: number | string, updatedCollection: CollectionDTO ): Promise + getCollectionFeaturedItems( + collectionIdOrAlias: number | string + ): Promise } diff --git a/src/collections/domain/useCases/GetCollectionFeaturedItems.ts b/src/collections/domain/useCases/GetCollectionFeaturedItems.ts new file mode 100644 index 00000000..21b66ef1 --- /dev/null +++ b/src/collections/domain/useCases/GetCollectionFeaturedItems.ts @@ -0,0 +1,25 @@ +import { UseCase } from '../../../core/domain/useCases/UseCase' +import { ICollectionsRepository } from '../repositories/ICollectionsRepository' +import { ROOT_COLLECTION_ID } from '../models/Collection' +import { CollectionFeaturedItem } from '../models/CollectionFeaturedItem' + +export class GetCollectionFeaturedItems implements UseCase { + private collectionsRepository: ICollectionsRepository + + constructor(collectionsRepository: ICollectionsRepository) { + this.collectionsRepository = collectionsRepository + } + + /** + * Returns a CollectionFeaturedItem array containing the featured items of the requested collection, given the collection identifier or alias. + * + * @param {number | string} [collectionIdOrAlias = ':root'] - A generic collection identifier, which can be either a string (for queries by CollectionAlias), or a number (for queries by CollectionId) + * If this parameter is not set, the default value is: ':root' + * @returns {Promise} + */ + async execute( + collectionIdOrAlias: number | string = ROOT_COLLECTION_ID + ): Promise { + return await this.collectionsRepository.getCollectionFeaturedItems(collectionIdOrAlias) + } +} diff --git a/src/collections/infra/repositories/CollectionsRepository.ts b/src/collections/infra/repositories/CollectionsRepository.ts index bee82863..fc77a833 100644 --- a/src/collections/infra/repositories/CollectionsRepository.ts +++ b/src/collections/infra/repositories/CollectionsRepository.ts @@ -17,6 +17,8 @@ import { SortType } from '../../domain/models/CollectionSearchCriteria' import { CollectionItemType } from '../../domain/models/CollectionItemType' +import { CollectionFeaturedItem } from '../../domain/models/CollectionFeaturedItem' +import { transformCollectionFeaturedItemsPayloadToCollectionFeaturedItems } from './transformers/collectionFeaturedItemsTransformer' export interface NewCollectionRequestPayload { alias: string @@ -240,4 +242,16 @@ export class CollectionsRepository extends ApiRepository implements ICollections }) } } + + public async getCollectionFeaturedItems( + collectionIdOrAlias: number | string + ): Promise { + return this.doGet(`/${this.collectionsResourceName}/${collectionIdOrAlias}/featuredItems`, true) + .then((response) => + transformCollectionFeaturedItemsPayloadToCollectionFeaturedItems(response.data.data) + ) + .catch((error) => { + throw error + }) + } } diff --git a/src/collections/infra/repositories/transformers/CollectionFeaturedItemPayload.ts b/src/collections/infra/repositories/transformers/CollectionFeaturedItemPayload.ts new file mode 100644 index 00000000..34ba0dcf --- /dev/null +++ b/src/collections/infra/repositories/transformers/CollectionFeaturedItemPayload.ts @@ -0,0 +1,7 @@ +export interface CollectionFeaturedItemPayload { + id: number + content: string + imageFileName: string | null + imageFileUrl: string | null + displayOrder: number +} diff --git a/src/collections/infra/repositories/transformers/collectionFeaturedItemsTransformer.ts b/src/collections/infra/repositories/transformers/collectionFeaturedItemsTransformer.ts new file mode 100644 index 00000000..ab8e1f70 --- /dev/null +++ b/src/collections/infra/repositories/transformers/collectionFeaturedItemsTransformer.ts @@ -0,0 +1,14 @@ +import { CollectionFeaturedItem } from '../../../domain/models/CollectionFeaturedItem' +import { CollectionFeaturedItemPayload } from './CollectionFeaturedItemPayload' + +export const transformCollectionFeaturedItemsPayloadToCollectionFeaturedItems = ( + collectionFeaturedItemsPayload: CollectionFeaturedItemPayload[] +): CollectionFeaturedItem[] => { + return collectionFeaturedItemsPayload.map((collectionFeaturedItemPayload) => ({ + id: collectionFeaturedItemPayload.id, + content: collectionFeaturedItemPayload.content, + imageFileUrl: collectionFeaturedItemPayload.imageFileUrl, + imageFileName: collectionFeaturedItemPayload.imageFileName, + displayOrder: collectionFeaturedItemPayload.displayOrder + })) +} From e7246c910d784e2031cd5fe2fd863fe9e112414a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Germ=C3=A1n=20Saracca?= Date: Fri, 3 Jan 2025 12:27:54 -0300 Subject: [PATCH 02/18] test: integration cases --- src/collections/index.ts | 7 +- test/environment/.env | 4 +- .../collections/CollectionsRepository.test.ts | 67 +++++++++++++++- .../collectionFeaturedItemsHelper.ts | 79 +++++++++++++++++++ 4 files changed, 152 insertions(+), 5 deletions(-) create mode 100644 test/testHelpers/collections/collectionFeaturedItemsHelper.ts diff --git a/src/collections/index.ts b/src/collections/index.ts index b148b55d..7e149b53 100644 --- a/src/collections/index.ts +++ b/src/collections/index.ts @@ -5,7 +5,7 @@ import { GetCollectionUserPermissions } from './domain/useCases/GetCollectionUse import { GetCollectionItems } from './domain/useCases/GetCollectionItems' import { PublishCollection } from './domain/useCases/PublishCollection' import { UpdateCollection } from './domain/useCases/UpdateCollection' - +import { GetCollectionFeaturedItems } from './domain/useCases/GetCollectionFeaturedItems' import { CollectionsRepository } from './infra/repositories/CollectionsRepository' const collectionsRepository = new CollectionsRepository() @@ -17,6 +17,7 @@ const getCollectionUserPermissions = new GetCollectionUserPermissions(collection const getCollectionItems = new GetCollectionItems(collectionsRepository) const publishCollection = new PublishCollection(collectionsRepository) const updateCollection = new UpdateCollection(collectionsRepository) +const getCollectionFeaturedItems = new GetCollectionFeaturedItems(collectionsRepository) export { getCollection, @@ -25,7 +26,8 @@ export { getCollectionUserPermissions, getCollectionItems, publishCollection, - updateCollection + updateCollection, + getCollectionFeaturedItems } export { Collection, CollectionInputLevel } from './domain/models/Collection' export { CollectionFacet } from './domain/models/CollectionFacet' @@ -34,3 +36,4 @@ 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' diff --git a/test/environment/.env b/test/environment/.env index 80e9a14e..bf08e36f 100644 --- a/test/environment/.env +++ b/test/environment/.env @@ -1,6 +1,6 @@ POSTGRES_VERSION=13 DATAVERSE_DB_USER=dataverse SOLR_VERSION=9.3.0 -DATAVERSE_IMAGE_REGISTRY=docker.io -DATAVERSE_IMAGE_TAG=unstable +DATAVERSE_IMAGE_REGISTRY=ghcr.io +DATAVERSE_IMAGE_TAG=10943-featured-items DATAVERSE_BOOTSTRAP_TIMEOUT=5m diff --git a/test/integration/collections/CollectionsRepository.test.ts b/test/integration/collections/CollectionsRepository.test.ts index e763e105..c67b75a3 100644 --- a/test/integration/collections/CollectionsRepository.test.ts +++ b/test/integration/collections/CollectionsRepository.test.ts @@ -28,6 +28,11 @@ import { OrderType, SortType } from '../../../src/collections/domain/models/CollectionSearchCriteria' +import { ROOT_COLLECTION_ID } from '../../../src/collections/domain/models/Collection' +import { + createCollectionFeaturedItemViaApi, + deleteCollectionFeaturedItemViaApi +} from '../../testHelpers/collections/collectionFeaturedItemsHelper' describe('CollectionsRepository', () => { const testCollectionAlias = 'collectionsRepositoryTestCollection' @@ -293,7 +298,7 @@ describe('CollectionsRepository', () => { const expectedFileMd5 = '68b22040025784da775f55cfcb6dee2e' const expectedDatasetCitationFragment = - 'Admin, Dataverse; Owner, Dataverse, 2024, "Dataset created using the createDataset use case' + 'Admin, Dataverse; Owner, Dataverse, 2025, "Dataset created using the createDataset use case' const expectedDatasetDescription = 'Dataset created using the createDataset use case' const expectedFileName = 'test-file-1.txt' const expectedCollectionsName = 'Scientific Research' @@ -720,4 +725,64 @@ describe('CollectionsRepository', () => { ).rejects.toThrow(expectedError) }) }) + + describe('getCollectionFeaturedItems', () => { + let tetFeaturedItemId: number + + beforeAll(async () => { + try { + const featuredItemCreated = await createCollectionFeaturedItemViaApi(testCollectionAlias, { + content: '

Test content

', + displayOrder: 1, + withFile: true, + fileName: 'featured-item-test-image.png' + }) + + tetFeaturedItemId = featuredItemCreated.id + } catch (error) { + throw new Error(`Error while creating collection featured item in ${testCollectionAlias}`) + } + }) + + afterAll(async () => { + try { + await deleteCollectionFeaturedItemViaApi(tetFeaturedItemId) + } catch (error) { + throw new Error( + `Tests afterAll(): Error while deleting test dataset with id ${tetFeaturedItemId}` + ) + } + }) + + 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) + + expect(featuredItemsResponse).toStrictEqual([]) + }) + + test('should return featured items array given a valid collection alias when collection has featured items', async () => { + const featuredItemsResponse = await sut.getCollectionFeaturedItems(testCollectionAlias) + console.log({ featuredItemsResponse }) + + expect(featuredItemsResponse.length).toBe(1) + expect(featuredItemsResponse[0].id).toBe(tetFeaturedItemId) + expect(featuredItemsResponse[0].displayOrder).toBe(1) + expect(featuredItemsResponse[0].content).toBe('

Test content

') + expect(featuredItemsResponse[0].imageFileUrl).toBe( + 'http://localhost:8080/api/access/dataverseFeatureItemImage/1' + ) + expect(featuredItemsResponse[0].imageFileName).toBe('featured-item-test-image.png') + }) + + test('should return error when collection does not exist', async () => { + const invalidCollectionAlias = 'invalid-collection-alias' + const expectedError = new ReadError( + `[404] Can't find dataverse with identifier='${invalidCollectionAlias}'` + ) + + await expect(sut.getCollectionFeaturedItems(invalidCollectionAlias)).rejects.toThrow( + expectedError + ) + }) + }) }) diff --git a/test/testHelpers/collections/collectionFeaturedItemsHelper.ts b/test/testHelpers/collections/collectionFeaturedItemsHelper.ts new file mode 100644 index 00000000..1a20ce77 --- /dev/null +++ b/test/testHelpers/collections/collectionFeaturedItemsHelper.ts @@ -0,0 +1,79 @@ +import axios from 'axios' +import { File, Blob } from '@web-std/file' +import { CollectionFeaturedItem } from '../../../src/collections/domain/models/CollectionFeaturedItem' +import { ROOT_COLLECTION_ID } from '../../../src/collections/domain/models/Collection' +import { TestConstants } from '../TestConstants' + +interface CreateCollectionFeaturedItemData { + content: string + displayOrder?: number + withFile?: boolean + fileName?: string +} + +export async function createCollectionFeaturedItemViaApi( + collectionAlias: string, + { + content, + displayOrder = 1, + withFile = false, + fileName = 'test-image.png' + }: CreateCollectionFeaturedItemData +): Promise { + try { + if (collectionAlias == undefined) { + collectionAlias = ROOT_COLLECTION_ID + } + + const formData = new FormData() + formData.append('content', content) + formData.append('displayOrder', displayOrder.toString()) + + if (withFile) { + const file = createImageFile(fileName) + + formData.append('file', file) + } + + return await axios + .post(`${TestConstants.TEST_API_URL}/dataverses/${collectionAlias}/featuredItem`, formData, { + headers: { + 'Content-Type': 'multipart/form-data', + 'X-Dataverse-Key': process.env.TEST_API_KEY + } + }) + .then((response) => 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( + `${TestConstants.TEST_API_URL}/dataverseFeaturedItems/${featuredItemId}`, + { + headers: { 'Content-Type': 'application/json', 'X-Dataverse-Key': process.env.TEST_API_KEY } + } + ) + } catch (error) { + throw new Error(`Error while deleting collection featured item with id ${featuredItemId}`) + } +} + +export function createImageFile(fileName = 'test-image.png'): File { + // Binary data for a 1x1 black pixel PNG image + const imageData = Uint8Array.from([ + 0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a, 0x00, 0x00, 0x00, 0x0d, 0x49, 0x48, 0x44, 0x52, + 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x08, 0x06, 0x00, 0x00, 0x00, 0x1f, 0x15, 0xc4, + 0x89, 0x00, 0x00, 0x00, 0x0a, 0x49, 0x44, 0x41, 0x54, 0x78, 0x9c, 0x63, 0x60, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x01, 0xe2, 0x21, 0xbc, 0x33, 0x00, 0x00, 0x00, 0x00, 0x49, 0x45, 0x4e, 0x44, 0xae, + 0x42, 0x60, 0x82 + ]) + + const blob = new Blob([imageData], { type: 'image/png' }) + const imageFile = new File([blob], fileName, { type: 'image/png' }) + + return imageFile +} From 1143cf11d24bc4a3b42d4efcc87b92fd850e5883 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Germ=C3=A1n=20Saracca?= Date: Fri, 3 Jan 2025 16:55:06 -0300 Subject: [PATCH 03/18] test: functional cases --- .../GetCollectionFeaturedItems.test.ts | 106 ++++++++++++++++++ .../collections/CollectionsRepository.test.ts | 10 +- 2 files changed, 111 insertions(+), 5 deletions(-) create mode 100644 test/functional/collections/GetCollectionFeaturedItems.test.ts diff --git a/test/functional/collections/GetCollectionFeaturedItems.test.ts b/test/functional/collections/GetCollectionFeaturedItems.test.ts new file mode 100644 index 00000000..5865710d --- /dev/null +++ b/test/functional/collections/GetCollectionFeaturedItems.test.ts @@ -0,0 +1,106 @@ +import { ApiConfig, ReadError, getCollectionFeaturedItems } from '../../../src' +import { TestConstants } from '../../testHelpers/TestConstants' +import { DataverseApiAuthMechanism } from '../../../src/core/infra/repositories/ApiConfig' +import { + createCollectionFeaturedItemViaApi, + deleteCollectionFeaturedItemViaApi +} from '../../testHelpers/collections/collectionFeaturedItemsHelper' +import { + createCollectionViaApi, + deleteCollectionViaApi +} from '../../testHelpers/collections/collectionHelper' +import { ROOT_COLLECTION_ID } from '../../../src/collections/domain/models/Collection' + +describe('execute', () => { + const testCollectionAlias = 'collectionsRepositoryTestCollection' + let testFeaturedItemId: number + + beforeEach(async () => { + ApiConfig.init( + TestConstants.TEST_API_URL, + DataverseApiAuthMechanism.API_KEY, + process.env.TEST_API_KEY + ) + }) + + beforeAll(async () => { + try { + await createCollectionViaApi(testCollectionAlias) + + const featuredItemCreated = await createCollectionFeaturedItemViaApi(testCollectionAlias, { + content: '

Test content

', + displayOrder: 1, + withFile: true, + fileName: 'featured-item-test-image.png' + }) + + testFeaturedItemId = featuredItemCreated.id + } catch (error) { + throw new Error(`Error while creating collection featured item in ${testCollectionAlias}`) + } + }) + + afterAll(async () => { + try { + await deleteCollectionFeaturedItemViaApi(testFeaturedItemId) + await deleteCollectionViaApi(testCollectionAlias) + } catch (error) { + throw new Error( + `Tests afterAll(): Error while deleting featured item with id ${testFeaturedItemId}` + ) + } + }) + + test('should return featured items array given a valid collection alias that has featured items', async () => { + const featuredItemsResponse = await getCollectionFeaturedItems.execute(testCollectionAlias) + + expect(featuredItemsResponse.length).toBe(1) + expect(featuredItemsResponse[0].id).toBe(testFeaturedItemId) + expect(featuredItemsResponse[0].displayOrder).toBe(1) + expect(featuredItemsResponse[0].content).toBe('

Test content

') + expect(featuredItemsResponse[0].imageFileUrl).toBe( + 'http://localhost:8080/api/access/dataverseFeatureItemImage/1' + ) + expect(featuredItemsResponse[0].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 featuredItemsResponse = await getCollectionFeaturedItems.execute(testCollectionAlias) + + expect(featuredItemsResponse.length).toBe(2) + expect(featuredItemsResponse[1].id).toBe(featuredItemCreated.id) + expect(featuredItemsResponse[1].displayOrder).toBe(2) + expect(featuredItemsResponse[1].content).toBe('

Test content

') + expect(featuredItemsResponse[1].imageFileUrl).toBeUndefined() + expect(featuredItemsResponse[1].imageFileName).toBeUndefined() + + await deleteCollectionFeaturedItemViaApi(featuredItemCreated.id) + }) + + test('should return empty featured items array given a valid collection alias that has no featured items', async () => { + const featuredItemsResponse = await getCollectionFeaturedItems.execute(ROOT_COLLECTION_ID) + + expect(featuredItemsResponse).toStrictEqual([]) + }) + + test('should throw an error when collection does not exist', async () => { + const invalidCollectionAlias = 'invalid-collection-alias' + let readError: ReadError | undefined + + try { + await getCollectionFeaturedItems.execute(invalidCollectionAlias) + } catch (error) { + readError = error + } finally { + expect(readError).toBeInstanceOf(ReadError) + expect((readError as ReadError).message).toEqual( + `There was an error when reading the resource. Reason was: [404] Can't find dataverse with identifier='${invalidCollectionAlias}'` + ) + } + }) +}) diff --git a/test/integration/collections/CollectionsRepository.test.ts b/test/integration/collections/CollectionsRepository.test.ts index c67b75a3..c2f1fe97 100644 --- a/test/integration/collections/CollectionsRepository.test.ts +++ b/test/integration/collections/CollectionsRepository.test.ts @@ -727,7 +727,7 @@ describe('CollectionsRepository', () => { }) describe('getCollectionFeaturedItems', () => { - let tetFeaturedItemId: number + let testFeaturedItemId: number beforeAll(async () => { try { @@ -738,7 +738,7 @@ describe('CollectionsRepository', () => { fileName: 'featured-item-test-image.png' }) - tetFeaturedItemId = featuredItemCreated.id + testFeaturedItemId = featuredItemCreated.id } catch (error) { throw new Error(`Error while creating collection featured item in ${testCollectionAlias}`) } @@ -746,10 +746,10 @@ describe('CollectionsRepository', () => { afterAll(async () => { try { - await deleteCollectionFeaturedItemViaApi(tetFeaturedItemId) + await deleteCollectionFeaturedItemViaApi(testFeaturedItemId) } catch (error) { throw new Error( - `Tests afterAll(): Error while deleting test dataset with id ${tetFeaturedItemId}` + `Tests afterAll(): Error while deleting featured item with id ${testFeaturedItemId}` ) } }) @@ -765,7 +765,7 @@ describe('CollectionsRepository', () => { console.log({ featuredItemsResponse }) expect(featuredItemsResponse.length).toBe(1) - expect(featuredItemsResponse[0].id).toBe(tetFeaturedItemId) + expect(featuredItemsResponse[0].id).toBe(testFeaturedItemId) expect(featuredItemsResponse[0].displayOrder).toBe(1) expect(featuredItemsResponse[0].content).toBe('

Test content

') expect(featuredItemsResponse[0].imageFileUrl).toBe( From 30dbbffb1052ed408abe499996f5ea7d760d2845 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Germ=C3=A1n=20Saracca?= Date: Fri, 3 Jan 2025 17:22:00 -0300 Subject: [PATCH 04/18] test: unit cases --- .../collectionFeaturedItemsHelper.ts | 39 +++++++++++++++++++ .../collections/collectionHelper.ts | 3 +- .../GetCollectionFeaturedItems.test.ts | 29 ++++++++++++++ 3 files changed, 70 insertions(+), 1 deletion(-) create mode 100644 test/unit/collections/GetCollectionFeaturedItems.test.ts diff --git a/test/testHelpers/collections/collectionFeaturedItemsHelper.ts b/test/testHelpers/collections/collectionFeaturedItemsHelper.ts index 1a20ce77..e9c7db2c 100644 --- a/test/testHelpers/collections/collectionFeaturedItemsHelper.ts +++ b/test/testHelpers/collections/collectionFeaturedItemsHelper.ts @@ -3,6 +3,7 @@ import { File, Blob } from '@web-std/file' import { CollectionFeaturedItem } from '../../../src/collections/domain/models/CollectionFeaturedItem' import { ROOT_COLLECTION_ID } from '../../../src/collections/domain/models/Collection' import { TestConstants } from '../TestConstants' +import { CollectionFeaturedItemPayload } from '../../../src/collections/infra/repositories/transformers/CollectionFeaturedItemPayload' interface CreateCollectionFeaturedItemData { content: string @@ -62,6 +63,44 @@ export async function deleteCollectionFeaturedItemViaApi(featuredItemId: number) } } +export const createCollectionFeaturedItemsModel = (): CollectionFeaturedItem[] => { + return [ + { + id: 1, + content: 'This is a featured item', + displayOrder: 1, + imageFileName: 'test-image.png', + imageFileUrl: 'http://localhost:8080/api/access/dataverseFeatureItemImage/1' + }, + { + id: 2, + content: 'This is another featured item', + displayOrder: 2, + imageFileName: undefined, + imageFileUrl: undefined + } + ] +} + +export const createCollectionFeaturedItemsPayload = (): CollectionFeaturedItemPayload[] => { + return [ + { + id: 1, + content: 'This is a featured item', + displayOrder: 1, + imageFileName: 'test-image.png', + imageFileUrl: 'http://localhost:8080/api/access/dataverseFeatureItemImage/1' + }, + { + id: 2, + content: 'This is another featured item', + displayOrder: 2, + imageFileName: null, + imageFileUrl: null + } + ] +} + export function createImageFile(fileName = 'test-image.png'): File { // Binary data for a 1x1 black pixel PNG image const imageData = Uint8Array.from([ diff --git a/test/testHelpers/collections/collectionHelper.ts b/test/testHelpers/collections/collectionHelper.ts index f2d2e5ae..65de5d4a 100644 --- a/test/testHelpers/collections/collectionHelper.ts +++ b/test/testHelpers/collections/collectionHelper.ts @@ -8,6 +8,8 @@ import { NewCollectionRequestPayload } from '../../../src/collections/infra/repo import { CollectionFacetPayload } from '../../../src/collections/infra/repositories/transformers/CollectionFacetPayload' import { CollectionType } from '../../../src/collections/domain/models/CollectionType' +export const ROOT_COLLECTION_ALIAS = 'root' + const COLLECTION_ID = 11111 const COLLECTION_IS_RELEASED = true const COLLECTION_ALIAS_STR = 'secondCollection' @@ -205,4 +207,3 @@ export const createCollectionFacetRequestPayload = (): CollectionFacetPayload => displayName: 'testDisplayName' } } -export const ROOT_COLLECTION_ALIAS = 'root' diff --git a/test/unit/collections/GetCollectionFeaturedItems.test.ts b/test/unit/collections/GetCollectionFeaturedItems.test.ts new file mode 100644 index 00000000..a8235051 --- /dev/null +++ b/test/unit/collections/GetCollectionFeaturedItems.test.ts @@ -0,0 +1,29 @@ +import { ICollectionsRepository } from '../../../src/collections/domain/repositories/ICollectionsRepository' +import { ReadError } from '../../../src' +import { createCollectionFeaturedItemsModel } from '../../testHelpers/collections/collectionFeaturedItemsHelper' +import { GetCollectionFeaturedItems } from '../../../src/collections/domain/useCases/GetCollectionFeaturedItems' + +describe('execute', () => { + test('should return collection featured items on repository success', async () => { + const testFeaturedItems = createCollectionFeaturedItemsModel() + const collectionRepositoryStub: ICollectionsRepository = {} as ICollectionsRepository + collectionRepositoryStub.getCollectionFeaturedItems = jest + .fn() + .mockResolvedValue(testFeaturedItems) + const testGetCollectionFeaturedItems = new GetCollectionFeaturedItems(collectionRepositoryStub) + + const actual = await testGetCollectionFeaturedItems.execute(1) + + expect(actual).toEqual(testFeaturedItems) + }) + + test('should return error result on repository error', async () => { + const collectionRepositoryStub: ICollectionsRepository = {} as ICollectionsRepository + collectionRepositoryStub.getCollectionFeaturedItems = jest + .fn() + .mockRejectedValue(new ReadError()) + const testGetCollectionFeaturedItems = new GetCollectionFeaturedItems(collectionRepositoryStub) + + await expect(testGetCollectionFeaturedItems.execute(1)).rejects.toThrow(ReadError) + }) +}) From 559c89cf2bbf3725ad7b4c3ba0d181996ad50f8f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Germ=C3=A1n=20Saracca?= Date: Fri, 3 Jan 2025 17:27:47 -0300 Subject: [PATCH 05/18] chore: add docs --- docs/useCases.md | 28 +++++++++++++++++++ .../useCases/GetCollectionFeaturedItems.ts | 2 +- 2 files changed, 29 insertions(+), 1 deletion(-) diff --git a/docs/useCases.md b/docs/useCases.md index 6faa4ca7..8fd39054 100644 --- a/docs/useCases.md +++ b/docs/useCases.md @@ -14,6 +14,7 @@ The different use cases currently available in the package are classified below, - [Get Collection Facets](#get-collection-facets) - [Get User Permissions on a Collection](#get-user-permissions-on-a-collection) - [List All Collection Items](#list-all-collection-items) + - [Get Collection Featured Items](#get-collection-featured-items) - [Collections write use cases](#collections-write-use-cases) - [Create a Collection](#create-a-collection) - [Update a Collection](#update-a-collection) @@ -202,6 +203,33 @@ This use case supports the following optional parameters depending on the search - **offset**: (number) Offset for pagination. - **collectionSearchCriteria**: ([CollectionSearchCriteria](../src/collections/domain/models/CollectionSearchCriteria.ts)) Supports filtering the collection items by different properties. +#### 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. + +##### Example call: + +```typescript +import { getCollectionFeaturedItems } from '@iqss/dataverse-client-javascript' + +const collectionIdOrAlias = 12345 + +getCollectionFeaturedItems + .execute(collectionId) + .then((featuredItems: CollectionFeaturedItem[]) => { + /* ... */ + }) + .catch((error: Error) => { + /* ... */ + }) +``` + +_See [use case](../src/collections/domain/useCases/GetCollectionFeaturedItems.ts)_ definition. + +The `collectionIdOrAlias` is a generic collection identifier, which can be either a string (for queries by CollectionAlias), or a number (for queries by CollectionId). + +If no collection identifier is specified, the default collection identifier; `:root` will be used. If you want to search for a different collection, you must add the collection identifier as a parameter in the use case call. + ### Collections Write Use Cases #### Create a Collection diff --git a/src/collections/domain/useCases/GetCollectionFeaturedItems.ts b/src/collections/domain/useCases/GetCollectionFeaturedItems.ts index 21b66ef1..7a2a0adc 100644 --- a/src/collections/domain/useCases/GetCollectionFeaturedItems.ts +++ b/src/collections/domain/useCases/GetCollectionFeaturedItems.ts @@ -15,7 +15,7 @@ export class GetCollectionFeaturedItems implements UseCase} + * @returns {Promise} */ async execute( collectionIdOrAlias: number | string = ROOT_COLLECTION_ID From 3f120186598221b878f2b20cbef530e190ec3e5f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Germ=C3=A1n=20Saracca?= Date: Tue, 7 Jan 2025 17:26:53 -0300 Subject: [PATCH 06/18] feat: add updateCollectionFeaturedItems use case --- docs/useCases.md | 27 +++++++++++ .../domain/dtos/CollectionFeaturedItemsDTO.ts | 9 ++++ .../repositories/ICollectionsRepository.ts | 5 +++ .../useCases/UpdateCollectionFeaturedItems.ts | 32 +++++++++++++ src/collections/index.ts | 6 ++- .../repositories/CollectionsRepository.ts | 45 +++++++++++++++++++ src/core/infra/repositories/ApiRepository.ts | 5 ++- 7 files changed, 126 insertions(+), 3 deletions(-) create mode 100644 src/collections/domain/dtos/CollectionFeaturedItemsDTO.ts create mode 100644 src/collections/domain/useCases/UpdateCollectionFeaturedItems.ts diff --git a/docs/useCases.md b/docs/useCases.md index 8fd39054..92cd9222 100644 --- a/docs/useCases.md +++ b/docs/useCases.md @@ -19,6 +19,7 @@ The different use cases currently available in the package are classified below, - [Create a Collection](#create-a-collection) - [Update a Collection](#update-a-collection) - [Publish a Collection](#publish-a-collection) + - [Update Collection Featured Items](#update-collection-featured-items) - [Datasets](#Datasets) - [Datasets read use cases](#datasets-read-use-cases) - [Get a Dataset](#get-a-dataset) @@ -313,6 +314,32 @@ The `collectionIdOrAlias` is a generic collection identifier, which can be eithe _See [use case](../src/collections/domain/useCases/PublishCollection.ts)_ definition. +#### Update Collection Featured Items + +Updates all featured items, given a collection identifier and a CollectionFeaturedItemsDTO. + +##### Example call: + +```typescript +import { updateCollectionFeaturedItems } from '@iqss/dataverse-client-javascript' + +/* ... */ + +const collectionIdOrAlias = 12345 + +updateCollectionFeaturedItems + .execute(collectionIdOrAlias) + .then((collectionFeaturedItems: CollectionFeaturedItem[]) => { + /* ... */ + }) + +/* ... */ +``` + +The `collectionIdOrAlias` is a generic collection identifier, which can be either a string (for queries by CollectionAlias), or a number (for queries by CollectionId). + +_See [use case](../src/collections/domain/useCases/UpdateCollectionFeaturedItems.ts)_ definition. + ## Datasets ### Datasets Read Use Cases diff --git a/src/collections/domain/dtos/CollectionFeaturedItemsDTO.ts b/src/collections/domain/dtos/CollectionFeaturedItemsDTO.ts new file mode 100644 index 00000000..7a60052d --- /dev/null +++ b/src/collections/domain/dtos/CollectionFeaturedItemsDTO.ts @@ -0,0 +1,9 @@ +export type CollectionFeaturedItemsDTO = CollectionFeaturedItemDTO[] + +export interface CollectionFeaturedItemDTO { + id?: number + content: string + displayOrder: number + file?: File + keepFile: boolean +} diff --git a/src/collections/domain/repositories/ICollectionsRepository.ts b/src/collections/domain/repositories/ICollectionsRepository.ts index 91d395aa..f12df3aa 100644 --- a/src/collections/domain/repositories/ICollectionsRepository.ts +++ b/src/collections/domain/repositories/ICollectionsRepository.ts @@ -1,4 +1,5 @@ import { CollectionDTO } from '../dtos/CollectionDTO' +import { CollectionFeaturedItemsDTO } from '../dtos/CollectionFeaturedItemsDTO' import { Collection } from '../models/Collection' import { CollectionFacet } from '../models/CollectionFacet' import { CollectionFeaturedItem } from '../models/CollectionFeaturedItem' @@ -30,4 +31,8 @@ export interface ICollectionsRepository { getCollectionFeaturedItems( collectionIdOrAlias: number | string ): Promise + updateCollectionFeaturedItems( + collectionIdOrAlias: number | string, + featuredItemDTOs: CollectionFeaturedItemsDTO + ): Promise } diff --git a/src/collections/domain/useCases/UpdateCollectionFeaturedItems.ts b/src/collections/domain/useCases/UpdateCollectionFeaturedItems.ts new file mode 100644 index 00000000..10d22b5a --- /dev/null +++ b/src/collections/domain/useCases/UpdateCollectionFeaturedItems.ts @@ -0,0 +1,32 @@ +import { UseCase } from '../../../core/domain/useCases/UseCase' +import { CollectionFeaturedItemsDTO } from '../dtos/CollectionFeaturedItemsDTO' +import { ROOT_COLLECTION_ID } from '../models/Collection' +import { CollectionFeaturedItem } from '../models/CollectionFeaturedItem' +import { ICollectionsRepository } from '../repositories/ICollectionsRepository' + +export class UpdateCollectionFeaturedItems implements UseCase { + private collectionsRepository: ICollectionsRepository + + constructor(collectionsRepository: ICollectionsRepository) { + this.collectionsRepository = collectionsRepository + } + + /** + * Updates all featured items, given a collection identifier and a CollectionFeaturedItemsDTO. + * + * @param {number | string} [collectionIdOrAlias = ':root'] - A generic collection identifier, which can be either a string (for queries by CollectionAlias), or a number (for queries by CollectionId) + * If this parameter is not set, the default value is: ':root' + * @param {CollectionFeaturedItemsDTO} [newCollectionFeaturedItems] - CollectionFeaturedItemsDTO 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 { + return await this.collectionsRepository.updateCollectionFeaturedItems( + collectionIdOrAlias, + featuredItemsDTO + ) + } +} diff --git a/src/collections/index.ts b/src/collections/index.ts index 7e149b53..99721a7d 100644 --- a/src/collections/index.ts +++ b/src/collections/index.ts @@ -7,6 +7,7 @@ import { PublishCollection } from './domain/useCases/PublishCollection' import { UpdateCollection } from './domain/useCases/UpdateCollection' import { GetCollectionFeaturedItems } from './domain/useCases/GetCollectionFeaturedItems' import { CollectionsRepository } from './infra/repositories/CollectionsRepository' +import { UpdateCollectionFeaturedItems } from './domain/useCases/UpdateCollectionFeaturedItems' const collectionsRepository = new CollectionsRepository() @@ -18,6 +19,7 @@ const getCollectionItems = new GetCollectionItems(collectionsRepository) const publishCollection = new PublishCollection(collectionsRepository) const updateCollection = new UpdateCollection(collectionsRepository) const getCollectionFeaturedItems = new GetCollectionFeaturedItems(collectionsRepository) +const updateCollectionFeaturedItems = new UpdateCollectionFeaturedItems(collectionsRepository) export { getCollection, @@ -27,7 +29,8 @@ export { getCollectionItems, publishCollection, updateCollection, - getCollectionFeaturedItems + getCollectionFeaturedItems, + updateCollectionFeaturedItems } export { Collection, CollectionInputLevel } from './domain/models/Collection' export { CollectionFacet } from './domain/models/CollectionFacet' @@ -37,3 +40,4 @@ 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' diff --git a/src/collections/infra/repositories/CollectionsRepository.ts b/src/collections/infra/repositories/CollectionsRepository.ts index fc77a833..ffd41fb4 100644 --- a/src/collections/infra/repositories/CollectionsRepository.ts +++ b/src/collections/infra/repositories/CollectionsRepository.ts @@ -19,6 +19,8 @@ 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 { ApiConstants } from '../../../core/infra/repositories/ApiConstants' export interface NewCollectionRequestPayload { alias: string @@ -254,4 +256,47 @@ export class CollectionsRepository extends ApiRepository implements ICollections throw error }) } + + public async updateCollectionFeaturedItems( + collectionIdOrAlias: number | string, + featuredItemsDTO: CollectionFeaturedItemsDTO + ): Promise { + const featuredItemsFormData = + this.defineUpdateCollectionFeaturedItemsRequestBody(featuredItemsDTO) + + return this.doPut( + `/${this.collectionsResourceName}/${collectionIdOrAlias}/featuredItems`, + featuredItemsFormData, + undefined, + ApiConstants.CONTENT_TYPE_MULTIPART_FORM_DATA + ) + .then((response) => + transformCollectionFeaturedItemsPayloadToCollectionFeaturedItems(response.data.data) + ) + .catch((error) => { + throw error + }) + } + + private defineUpdateCollectionFeaturedItemsRequestBody( + featuredItemsDTO: CollectionFeaturedItemsDTO + ): FormData { + const formData = new FormData() + + featuredItemsDTO.forEach((item) => { + const { id, content, displayOrder, file, keepFile } = item + + // TODO: We need to configure this project to use strict typescript rules or at least strictNullChecks: true + // id is inferred here as number but it should be a number | undefined, same for file + // This config change should be done in a separate issue because it will require changes in some other ts files + + formData.append(`items[${item.displayOrder}][id]`, id ? id.toString() : '0') + formData.append(`items[${item.displayOrder}][displayOrder]`, displayOrder.toString()) + formData.append(`items[${item.displayOrder}][content]`, content) + formData.append(`items[${item.displayOrder}][file]`, file ? file : 'false') + formData.append(`items[${item.displayOrder}][keepFile]`, keepFile.toString()) + }) + + return formData + } } diff --git a/src/core/infra/repositories/ApiRepository.ts b/src/core/infra/repositories/ApiRepository.ts index fb0726ac..9c59d615 100644 --- a/src/core/infra/repositories/ApiRepository.ts +++ b/src/core/infra/repositories/ApiRepository.ts @@ -30,9 +30,10 @@ export abstract class ApiRepository { public async doPut( apiEndpoint: string, data: string | object, - queryParams: object = {} + queryParams: object = {}, + contentType: string = ApiConstants.CONTENT_TYPE_APPLICATION_JSON ): Promise { - return await this.doRequest('put', apiEndpoint, data, queryParams) + return await this.doRequest('put', apiEndpoint, data, queryParams, contentType) } public async doDelete(apiEndpoint: string, queryParams: object = {}): Promise { From bf503b36439f6f4019653e3666cb06f92948f9d0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Germ=C3=A1n=20Saracca?= Date: Mon, 13 Jan 2025 11:01:35 -0300 Subject: [PATCH 07/18] feat: update collection featured items --- .../repositories/CollectionsRepository.ts | 34 +++--- .../collectionFeaturedItemsTransformer.ts | 16 +-- .../infra/repositories/apiConfigBuilders.ts | 5 + .../collections/CollectionsRepository.test.ts | 106 +++++++++++++++++- .../collectionFeaturedItemsHelper.ts | 27 ++++- 5 files changed, 162 insertions(+), 26 deletions(-) diff --git a/src/collections/infra/repositories/CollectionsRepository.ts b/src/collections/infra/repositories/CollectionsRepository.ts index ffd41fb4..ebca9566 100644 --- a/src/collections/infra/repositories/CollectionsRepository.ts +++ b/src/collections/infra/repositories/CollectionsRepository.ts @@ -22,6 +22,8 @@ import { transformCollectionFeaturedItemsPayloadToCollectionFeaturedItems } from import { CollectionFeaturedItemsDTO } from '../../domain/dtos/CollectionFeaturedItemsDTO' import { ApiConstants } from '../../../core/infra/repositories/ApiConstants' +// TODO:ME - Create Delete All Featured Items use case + export interface NewCollectionRequestPayload { alias: string name: string @@ -261,8 +263,7 @@ export class CollectionsRepository extends ApiRepository implements ICollections collectionIdOrAlias: number | string, featuredItemsDTO: CollectionFeaturedItemsDTO ): Promise { - const featuredItemsFormData = - this.defineUpdateCollectionFeaturedItemsRequestBody(featuredItemsDTO) + const featuredItemsFormData = this.toFeaturedItemsFormData(featuredItemsDTO) return this.doPut( `/${this.collectionsResourceName}/${collectionIdOrAlias}/featuredItems`, @@ -278,23 +279,24 @@ export class CollectionsRepository extends ApiRepository implements ICollections }) } - private defineUpdateCollectionFeaturedItemsRequestBody( - featuredItemsDTO: CollectionFeaturedItemsDTO - ): FormData { + private toFeaturedItemsFormData(featuredItemsDTO: CollectionFeaturedItemsDTO): FormData { + // This is not really necessary because we are sending displayOrder property anyways, but I wanted to keep the order of the items in the form data + const orderedFeaturedItemsDTO = featuredItemsDTO.sort((a, b) => a.displayOrder - b.displayOrder) + const formData = new FormData() - featuredItemsDTO.forEach((item) => { + orderedFeaturedItemsDTO.forEach((item) => { const { id, content, displayOrder, file, keepFile } = item - - // TODO: We need to configure this project to use strict typescript rules or at least strictNullChecks: true - // id is inferred here as number but it should be a number | undefined, same for file - // This config change should be done in a separate issue because it will require changes in some other ts files - - formData.append(`items[${item.displayOrder}][id]`, id ? id.toString() : '0') - formData.append(`items[${item.displayOrder}][displayOrder]`, displayOrder.toString()) - formData.append(`items[${item.displayOrder}][content]`, content) - formData.append(`items[${item.displayOrder}][file]`, file ? file : 'false') - formData.append(`items[${item.displayOrder}][keepFile]`, keepFile.toString()) + 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 ab8e1f70..3428f6be 100644 --- a/src/collections/infra/repositories/transformers/collectionFeaturedItemsTransformer.ts +++ b/src/collections/infra/repositories/transformers/collectionFeaturedItemsTransformer.ts @@ -4,11 +4,13 @@ import { CollectionFeaturedItemPayload } from './CollectionFeaturedItemPayload' export const transformCollectionFeaturedItemsPayloadToCollectionFeaturedItems = ( collectionFeaturedItemsPayload: CollectionFeaturedItemPayload[] ): CollectionFeaturedItem[] => { - return collectionFeaturedItemsPayload.map((collectionFeaturedItemPayload) => ({ - id: collectionFeaturedItemPayload.id, - content: collectionFeaturedItemPayload.content, - imageFileUrl: collectionFeaturedItemPayload.imageFileUrl, - imageFileName: collectionFeaturedItemPayload.imageFileName, - displayOrder: collectionFeaturedItemPayload.displayOrder - })) + return collectionFeaturedItemsPayload + .map((collectionFeaturedItemPayload) => ({ + id: collectionFeaturedItemPayload.id, + content: collectionFeaturedItemPayload.content, + imageFileUrl: collectionFeaturedItemPayload.imageFileUrl, + imageFileName: collectionFeaturedItemPayload.imageFileName, + displayOrder: collectionFeaturedItemPayload.displayOrder + })) + .sort((a, b) => a.displayOrder - b.displayOrder) } diff --git a/src/core/infra/repositories/apiConfigBuilders.ts b/src/core/infra/repositories/apiConfigBuilders.ts index 16f68d7d..4ee43504 100644 --- a/src/core/infra/repositories/apiConfigBuilders.ts +++ b/src/core/infra/repositories/apiConfigBuilders.ts @@ -16,6 +16,11 @@ export const buildRequestConfig = ( ...(abortSignal && { signal: abortSignal }) } + // When using multipart/form-data for axios to work properly its better to avoid setting the content-type and let the browser manage it + if (contentType === ApiConstants.CONTENT_TYPE_MULTIPART_FORM_DATA) { + requestConfig.headers['Content-Type'] = undefined + } + if (!authRequired) { return requestConfig } diff --git a/test/integration/collections/CollectionsRepository.test.ts b/test/integration/collections/CollectionsRepository.test.ts index c2f1fe97..36988a35 100644 --- a/test/integration/collections/CollectionsRepository.test.ts +++ b/test/integration/collections/CollectionsRepository.test.ts @@ -1,6 +1,7 @@ import { CollectionsRepository } from '../../../src/collections/infra/repositories/CollectionsRepository' import { TestConstants } from '../../testHelpers/TestConstants' import { + CollectionFeaturedItemsDTO, CollectionItemType, CollectionPreview, CollectionSearchCriteria, @@ -31,6 +32,8 @@ import { import { ROOT_COLLECTION_ID } from '../../../src/collections/domain/models/Collection' import { createCollectionFeaturedItemViaApi, + createImageFile, + deleteAllCollectionFeaturedItemsViaApi, deleteCollectionFeaturedItemViaApi } from '../../testHelpers/collections/collectionFeaturedItemsHelper' @@ -762,7 +765,6 @@ describe('CollectionsRepository', () => { test('should return featured items array given a valid collection alias when collection has featured items', async () => { const featuredItemsResponse = await sut.getCollectionFeaturedItems(testCollectionAlias) - console.log({ featuredItemsResponse }) expect(featuredItemsResponse.length).toBe(1) expect(featuredItemsResponse[0].id).toBe(testFeaturedItemId) @@ -785,4 +787,106 @@ describe('CollectionsRepository', () => { ) }) }) + + describe('updateCollectionFeaturedItems', () => { + afterAll(async () => { + try { + await deleteAllCollectionFeaturedItemsViaApi(testCollectionAlias) + } catch (error) { + throw new Error( + `Tests afterAll(): Error while deleting all featured items from collection: ${testCollectionAlias}` + ) + } + }) + + it('should update collection featured items sending all new items', async () => { + const newFeaturedItems: CollectionFeaturedItemsDTO = [ + { + content: '

Test content 1

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

Test content 2

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

Test content 3

', + displayOrder: 2, + file: createImageFile('featured-item-test-image-3.png'), + keepFile: false + } + ] + + const response = await sut.updateCollectionFeaturedItems( + testCollectionAlias, + newFeaturedItems + ) + + 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).toContain( + 'http://localhost:8080/api/access/dataverseFeatureItemImage/' + ) + }) + }) + + // describe('deleteAllCollectionFeaturedItems', () => { + + // beforeAll(async () => { + // try { + // await createCollectionFeaturedItemViaApi(testCollectionAlias, { + // content: '

Test content

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

Test content 2

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

Test content 3

', + // displayOrder: 3, + // withFile: false + // }) + + // } catch (error) { + // throw new Error( + // `Tests afterAll(): Error while deleting all featured items from collection: ${testCollectionAlias}` + // ) + // } + // }) + + // it('should delete all collection featured items', async () => { + // const featuredItemsResponseFirst = await sut.getCollectionFeaturedItems(testCollectionAlias) + + // console.log({ featuredItemsResponseFirst }) + + // await deleteAllCollectionFeaturedItemsViaApi(testCollectionAlias) + + // const featuredItemsResponse = await sut.getCollectionFeaturedItems(testCollectionAlias) + + // console.log({ featuredItemsResponse }) + + // expect(featuredItemsResponse).toStrictEqual([]) + // }) + // }) }) diff --git a/test/testHelpers/collections/collectionFeaturedItemsHelper.ts b/test/testHelpers/collections/collectionFeaturedItemsHelper.ts index e9c7db2c..1e26cfdb 100644 --- a/test/testHelpers/collections/collectionFeaturedItemsHelper.ts +++ b/test/testHelpers/collections/collectionFeaturedItemsHelper.ts @@ -43,7 +43,10 @@ export async function createCollectionFeaturedItemViaApi( 'X-Dataverse-Key': process.env.TEST_API_KEY } }) - .then((response) => response.data.data) + .then((response) => { + // console.log({ singleFeatItemCreated: JSON.stringify(response.data.data) }) + return response.data.data + }) } catch (error) { console.log(error) throw new Error(`Error while creating collection featured item in ${collectionAlias}`) @@ -53,16 +56,36 @@ export async function createCollectionFeaturedItemViaApi( export async function deleteCollectionFeaturedItemViaApi(featuredItemId: number): Promise { try { return await axios.delete( - `${TestConstants.TEST_API_URL}/dataverseFeaturedItems/${featuredItemId}`, + `${TestConstants.TEST_API_URL}/dataverseFeaturedItems/${featuredItemId.toString()}`, { headers: { 'Content-Type': 'application/json', 'X-Dataverse-Key': process.env.TEST_API_KEY } } ) + // .then((resp) => { + // console.log({ deletedResp: JSON.stringify(resp.data) }) + // }) } catch (error) { throw new Error(`Error while deleting collection featured item with id ${featuredItemId}`) } } +export async function deleteAllCollectionFeaturedItemsViaApi(collectionAlias: string) { + try { + return await axios.delete( + `${TestConstants.TEST_API_URL}/dataverses/${collectionAlias}/featuredItems`, + { + headers: { 'Content-Type': 'application/json', 'X-Dataverse-Key': process.env.TEST_API_KEY } + } + ) + // .then((resp) => { + // console.log({ deletedAllResp: JSON.stringify(resp.data) }) + // }) + } catch (error) { + console.log(error) + throw new Error(`Error while deleting all featured items from collection: ${collectionAlias}`) + } +} + export const createCollectionFeaturedItemsModel = (): CollectionFeaturedItem[] => { return [ { From cd4f814c3be025016d3571e6d597c019e1db921e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Germ=C3=A1n=20Saracca?= Date: Mon, 13 Jan 2025 17:27:56 -0300 Subject: [PATCH 08/18] test: update collection featured items unit, func and integr --- .../repositories/ICollectionsRepository.ts | 1 + .../repositories/CollectionsRepository.ts | 8 + .../GetCollectionFeaturedItems.test.ts | 4 +- .../UpdateCollectionFeaturedItems.test.ts | 330 ++++++++++++++++++ .../collections/CollectionsRepository.test.ts | 14 +- .../collectionFeaturedItemsHelper.ts | 12 +- .../UpdateCollectionFeaturedItems.test.ts | 42 +++ 7 files changed, 396 insertions(+), 15 deletions(-) create mode 100644 test/functional/collections/UpdateCollectionFeaturedItems.test.ts create mode 100644 test/unit/collections/UpdateCollectionFeaturedItems.test.ts diff --git a/src/collections/domain/repositories/ICollectionsRepository.ts b/src/collections/domain/repositories/ICollectionsRepository.ts index f12df3aa..4c8fb3e9 100644 --- a/src/collections/domain/repositories/ICollectionsRepository.ts +++ b/src/collections/domain/repositories/ICollectionsRepository.ts @@ -35,4 +35,5 @@ export interface ICollectionsRepository { collectionIdOrAlias: number | string, featuredItemDTOs: CollectionFeaturedItemsDTO ): Promise + // deleteCollectionFeaturedItems(collectionIdOrAlias: number | string): Promise } diff --git a/src/collections/infra/repositories/CollectionsRepository.ts b/src/collections/infra/repositories/CollectionsRepository.ts index ebca9566..f8cf1492 100644 --- a/src/collections/infra/repositories/CollectionsRepository.ts +++ b/src/collections/infra/repositories/CollectionsRepository.ts @@ -301,4 +301,12 @@ export class CollectionsRepository extends ApiRepository implements ICollections return formData } + + // public async deleteCollectionFeaturedItems(collectionIdOrAlias: number | string): Promise { + // return this.doDelete(`/${this.collectionsResourceName}/${collectionIdOrAlias}/featuredItems`) + // .then(() => undefined) + // .catch((error) => { + // throw error + // }) + // } } diff --git a/test/functional/collections/GetCollectionFeaturedItems.test.ts b/test/functional/collections/GetCollectionFeaturedItems.test.ts index 5865710d..d389d7cd 100644 --- a/test/functional/collections/GetCollectionFeaturedItems.test.ts +++ b/test/functional/collections/GetCollectionFeaturedItems.test.ts @@ -57,7 +57,7 @@ describe('execute', () => { 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].content).toBe('

Test content

') expect(featuredItemsResponse[0].imageFileUrl).toBe( 'http://localhost:8080/api/access/dataverseFeatureItemImage/1' ) @@ -75,7 +75,7 @@ describe('execute', () => { 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].content).toBe('

Test content

') expect(featuredItemsResponse[1].imageFileUrl).toBeUndefined() expect(featuredItemsResponse[1].imageFileName).toBeUndefined() diff --git a/test/functional/collections/UpdateCollectionFeaturedItems.test.ts b/test/functional/collections/UpdateCollectionFeaturedItems.test.ts new file mode 100644 index 00000000..22f29005 --- /dev/null +++ b/test/functional/collections/UpdateCollectionFeaturedItems.test.ts @@ -0,0 +1,330 @@ +import { + ApiConfig, + CollectionFeaturedItemsDTO, + updateCollectionFeaturedItems, + WriteError +} from '../../../src' +import { TestConstants } from '../../testHelpers/TestConstants' +import { DataverseApiAuthMechanism } from '../../../src/core/infra/repositories/ApiConfig' +import { + createCollectionFeaturedItemViaApi, + createImageFile, + deleteCollectionFeaturedItemViaApi +} from '../../testHelpers/collections/collectionFeaturedItemsHelper' +import { + createCollectionViaApi, + deleteCollectionViaApi +} from '../../testHelpers/collections/collectionHelper' + +//TODO:ME - After content sanitization is fixed in backend, check if the content is being updated correctly keeping classes, href, rel, target, etc attributes. + +describe('execute', () => { + const testCollectionAlias = 'collectionsRepositoryTestCollection' + + beforeEach(async () => { + ApiConfig.init( + TestConstants.TEST_API_URL, + DataverseApiAuthMechanism.API_KEY, + process.env.TEST_API_KEY + ) + }) + + beforeAll(async () => { + try { + await createCollectionViaApi(testCollectionAlias) + } 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}` + ) + } + }) + + it('should successfully update the featured items of a collection', async () => { + const newFeaturedItems: CollectionFeaturedItemsDTO = [ + { + content: '

Test content 1

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

Test content 2

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

Test content 3

', + displayOrder: 2, + file: createImageFile('featured-item-test-image-3.png'), + keepFile: false + } + ] + + const updatedFeaturedItemsResponse = await updateCollectionFeaturedItems.execute( + testCollectionAlias, + newFeaturedItems + ) + + expect(updatedFeaturedItemsResponse.length).toBe(3) + + // expect(updatedFeaturedItemsResponse[0].content).toBe(newFeaturedItems[0].content) + expect(updatedFeaturedItemsResponse[0].displayOrder).toBe(newFeaturedItems[0].displayOrder) + expect(updatedFeaturedItemsResponse[0].imageFileUrl).toBeUndefined() + expect(updatedFeaturedItemsResponse[0].imageFileName).toBeUndefined() + + // 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(updatedFeaturedItemsResponse[2].content).toBe(newFeaturedItems[2].content) + expect(updatedFeaturedItemsResponse[2].displayOrder).toBe(newFeaturedItems[2].displayOrder) + expect(updatedFeaturedItemsResponse[2].imageFileName).toEqual('featured-item-test-image-3.png') + expect(updatedFeaturedItemsResponse[2].imageFileUrl).toBe( + `http://localhost:8080/api/access/dataverseFeatureItemImage/${updatedFeaturedItemsResponse[2].id}` + ) + }) + + test('should throw an error when collection does not exist', async () => { + const newFeaturedItems: CollectionFeaturedItemsDTO = [ + { + content: '

Test content 1

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

Test content 2

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

Test content 3

', + displayOrder: 2, + file: createImageFile('featured-item-test-image-3.png'), + keepFile: false + } + ] + const invalidCollectionAlias = 'invalid-collection-alias' + let writeError: WriteError | undefined + + try { + await updateCollectionFeaturedItems.execute(invalidCollectionAlias, newFeaturedItems) + } catch (error) { + writeError = error + } finally { + expect(writeError).toBeInstanceOf(WriteError) + expect((writeError as WriteError).message).toEqual( + `There was an error when writing the resource. Reason was: [404] Can't find dataverse with identifier='${invalidCollectionAlias}'` + ) + } + }) + + // TODO:ME - Returning a 500 instead of a proper error message indicating which item property has an error. + // test('should throw an error when featured item content is empty', async () => { + // const newFeaturedItems: CollectionFeaturedItemsDTO = [ + // { + // content: '', + // displayOrder: 0, + // file: undefined, + // keepFile: false + // } + // ] + // let writeError: WriteError | undefined + + // try { + // await updateCollectionFeaturedItems.execute(testCollectionAlias, newFeaturedItems) + // } catch (error) { + // writeError = error + // } finally { + // expect(writeError).toBeInstanceOf(WriteError) + // expect((writeError as WriteError).message).toEqual( + // 'There was an error when writing the resource. Reason was: [400] Content cannot be empty' + // ) + // } + // }) + + // TODO:ME - Currently not throwing an error and saving them with the same display order. + // test('should throw an error when featured items have the same display order', async () => { + // const newFeaturedItems: CollectionFeaturedItemsDTO = [ + // { + // content: '

Test content 1

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

Test content 2

', + // displayOrder: 0, + // file: undefined, + // keepFile: false + // } + // ] + // let writeError: WriteError | undefined + + // try { + // const resp = await updateCollectionFeaturedItems.execute( + // testCollectionAlias, + // newFeaturedItems + // ) + // console.log({ resp }) + // } catch (error) { + // writeError = error + // } finally { + // expect(writeError).toBeInstanceOf(WriteError) + // expect((writeError as WriteError).message).toEqual( + // 'There was an error when writing the resource. Reason was: [400] Display order must be unique' + // ) + // } + // }) + + describe('keepFile behaviour', () => { + let testFeaturedItemId: number + + const testFeaturedItemContent = '

Test content

' + const testFeaturedItemFilename = 'featured-item-test-image.png' + + beforeEach(async () => { + try { + const featuredItemCreated = await createCollectionFeaturedItemViaApi(testCollectionAlias, { + content: testFeaturedItemContent, + displayOrder: 1, + withFile: true, + fileName: testFeaturedItemFilename + }) + + testFeaturedItemId = featuredItemCreated.id + } catch (error) { + throw new Error(`Error while creating collection featured item in ${testCollectionAlias}`) + } + }) + + afterEach(async () => { + try { + await deleteCollectionFeaturedItemViaApi(testFeaturedItemId) + } catch (error) { + throw new Error( + `Tests afterAll(): Error while deleting featured item with id ${testFeaturedItemId}` + ) + } + }) + + it('should keep existing file for a featured item if file is undefined and keepFile is true', async () => { + const newFeaturedItems: CollectionFeaturedItemsDTO = [ + { + id: testFeaturedItemId, + content: '

Test content Updated

', + displayOrder: 0, + file: undefined, + keepFile: true + } + ] + + const updatedFeaturedItemsResponse = await updateCollectionFeaturedItems.execute( + testCollectionAlias, + newFeaturedItems + ) + + expect(updatedFeaturedItemsResponse.length).toBe(1) + + // expect(updatedFeaturedItemsResponse[0].content).toBe(newFeaturedItems[0].content) + expect(updatedFeaturedItemsResponse[0].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/dataverseFeatureItemImage/${updatedFeaturedItemsResponse[0].id}` + ) + }) + + it('should remove existing file for a featured item if file is undefined and keepFile is false', async () => { + const newFeaturedItems: CollectionFeaturedItemsDTO = [ + { + id: testFeaturedItemId, + content: '

Test content Updated

', + displayOrder: 0, + file: undefined, + keepFile: false + } + ] + + const updatedFeaturedItemsResponse = await updateCollectionFeaturedItems.execute( + testCollectionAlias, + newFeaturedItems + ) + + 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() + }) + + 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, + content: '

Test content Updated

', + displayOrder: 0, + file: createImageFile('featured-item-test-image-updated.png'), + keepFile: false + } + ] + + const updatedFeaturedItemsResponse = await updateCollectionFeaturedItems.execute( + testCollectionAlias, + newFeaturedItems + ) + + 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/dataverseFeatureItemImage/${updatedFeaturedItemsResponse[0].id}` + ) + }) + + it('should not replace existing file for a featured item if a new file is provided but keepFile is true', async () => { + const newFeaturedItems: CollectionFeaturedItemsDTO = [ + { + id: testFeaturedItemId, + content: '

Test content Updated

', + displayOrder: 0, + file: createImageFile('featured-item-test-image-updated.png'), + keepFile: true + } + ] + + const updatedFeaturedItemsResponse = await updateCollectionFeaturedItems.execute( + testCollectionAlias, + newFeaturedItems + ) + + expect(updatedFeaturedItemsResponse.length).toBe(1) + + // expect(updatedFeaturedItemsResponse[0].content).toBe(newFeaturedItems[0].content) + expect(updatedFeaturedItemsResponse[0].displayOrder).toBe(newFeaturedItems[0].displayOrder) + // Should keep the existing file even if a file was provided because keepFile is true + expect(updatedFeaturedItemsResponse[0].imageFileName).toEqual(testFeaturedItemFilename) + expect(updatedFeaturedItemsResponse[0].imageFileUrl).toBe( + `http://localhost:8080/api/access/dataverseFeatureItemImage/${updatedFeaturedItemsResponse[0].id}` + ) + }) + }) +}) diff --git a/test/integration/collections/CollectionsRepository.test.ts b/test/integration/collections/CollectionsRepository.test.ts index 36988a35..170a1990 100644 --- a/test/integration/collections/CollectionsRepository.test.ts +++ b/test/integration/collections/CollectionsRepository.test.ts @@ -769,9 +769,9 @@ describe('CollectionsRepository', () => { 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].content).toBe('

Test content

') expect(featuredItemsResponse[0].imageFileUrl).toBe( - 'http://localhost:8080/api/access/dataverseFeatureItemImage/1' + `http://localhost:8080/api/access/dataverseFeatureItemImage/${featuredItemsResponse[0].id}` ) expect(featuredItemsResponse[0].imageFileName).toBe('featured-item-test-image.png') }) @@ -828,21 +828,21 @@ describe('CollectionsRepository', () => { expect(response).toHaveLength(3) - expect(response[0].content).toEqual(newFeaturedItems[0].content) + // 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].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].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).toContain( - 'http://localhost:8080/api/access/dataverseFeatureItemImage/' + expect(response[2].imageFileUrl).toBe( + `http://localhost:8080/api/access/dataverseFeatureItemImage/${response[2].id}` ) }) }) diff --git a/test/testHelpers/collections/collectionFeaturedItemsHelper.ts b/test/testHelpers/collections/collectionFeaturedItemsHelper.ts index 1e26cfdb..8a4b5d40 100644 --- a/test/testHelpers/collections/collectionFeaturedItemsHelper.ts +++ b/test/testHelpers/collections/collectionFeaturedItemsHelper.ts @@ -3,7 +3,7 @@ import { File, Blob } from '@web-std/file' import { CollectionFeaturedItem } from '../../../src/collections/domain/models/CollectionFeaturedItem' import { ROOT_COLLECTION_ID } from '../../../src/collections/domain/models/Collection' import { TestConstants } from '../TestConstants' -import { CollectionFeaturedItemPayload } from '../../../src/collections/infra/repositories/transformers/CollectionFeaturedItemPayload' +import { CollectionFeaturedItemsDTO } from '../../../src' interface CreateCollectionFeaturedItemData { content: string @@ -105,21 +105,21 @@ export const createCollectionFeaturedItemsModel = (): CollectionFeaturedItem[] = ] } -export const createCollectionFeaturedItemsPayload = (): CollectionFeaturedItemPayload[] => { +export const createCollectionFeaturedItemsDTO = (): CollectionFeaturedItemsDTO => { return [ { id: 1, content: 'This is a featured item', displayOrder: 1, - imageFileName: 'test-image.png', - imageFileUrl: 'http://localhost:8080/api/access/dataverseFeatureItemImage/1' + file: createImageFile(), + keepFile: false }, { id: 2, content: 'This is another featured item', displayOrder: 2, - imageFileName: null, - imageFileUrl: null + file: undefined, + keepFile: false } ] } diff --git a/test/unit/collections/UpdateCollectionFeaturedItems.test.ts b/test/unit/collections/UpdateCollectionFeaturedItems.test.ts new file mode 100644 index 00000000..02e81a0d --- /dev/null +++ b/test/unit/collections/UpdateCollectionFeaturedItems.test.ts @@ -0,0 +1,42 @@ +import { ICollectionsRepository } from '../../../src/collections/domain/repositories/ICollectionsRepository' +import { ReadError } from '../../../src' +import { + createCollectionFeaturedItemsDTO, + createCollectionFeaturedItemsModel +} from '../../testHelpers/collections/collectionFeaturedItemsHelper' +import { UpdateCollectionFeaturedItems } from '../../../src/collections/domain/useCases/UpdateCollectionFeaturedItems' + +const testFeaturedItemsDTO = createCollectionFeaturedItemsDTO() + +describe('execute', () => { + test('should update collection featured items on repository success', async () => { + const testFeaturedItems = createCollectionFeaturedItemsModel() + + const collectionRepositoryStub: ICollectionsRepository = {} as ICollectionsRepository + collectionRepositoryStub.updateCollectionFeaturedItems = jest + .fn() + .mockResolvedValue(testFeaturedItems) + + const testGetCollectionFeaturedItems = new UpdateCollectionFeaturedItems( + collectionRepositoryStub + ) + + const actual = await testGetCollectionFeaturedItems.execute(1, testFeaturedItemsDTO) + + expect(actual).toEqual(testFeaturedItems) + }) + + test('should return error result on repository error', async () => { + const collectionRepositoryStub: ICollectionsRepository = {} as ICollectionsRepository + collectionRepositoryStub.updateCollectionFeaturedItems = jest + .fn() + .mockRejectedValue(new ReadError()) + const testUpdateCollectionFeaturedItems = new UpdateCollectionFeaturedItems( + collectionRepositoryStub + ) + + await expect( + testUpdateCollectionFeaturedItems.execute(1, testFeaturedItemsDTO) + ).rejects.toThrow(ReadError) + }) +}) From f58eed659f8af46817e154d38ddd1168d2b6e45d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Germ=C3=A1n=20Saracca?= Date: Mon, 13 Jan 2025 17:30:03 -0300 Subject: [PATCH 09/18] small change to run action --- .../domain/repositories/ICollectionsRepository.ts | 2 +- .../infra/repositories/CollectionsRepository.ts | 14 +++++++------- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/collections/domain/repositories/ICollectionsRepository.ts b/src/collections/domain/repositories/ICollectionsRepository.ts index 4c8fb3e9..85f6c42f 100644 --- a/src/collections/domain/repositories/ICollectionsRepository.ts +++ b/src/collections/domain/repositories/ICollectionsRepository.ts @@ -35,5 +35,5 @@ export interface ICollectionsRepository { collectionIdOrAlias: number | string, featuredItemDTOs: CollectionFeaturedItemsDTO ): Promise - // deleteCollectionFeaturedItems(collectionIdOrAlias: number | string): Promise + deleteCollectionFeaturedItems(collectionIdOrAlias: number | string): Promise } diff --git a/src/collections/infra/repositories/CollectionsRepository.ts b/src/collections/infra/repositories/CollectionsRepository.ts index f8cf1492..3c819b7d 100644 --- a/src/collections/infra/repositories/CollectionsRepository.ts +++ b/src/collections/infra/repositories/CollectionsRepository.ts @@ -302,11 +302,11 @@ export class CollectionsRepository extends ApiRepository implements ICollections return formData } - // public async deleteCollectionFeaturedItems(collectionIdOrAlias: number | string): Promise { - // return this.doDelete(`/${this.collectionsResourceName}/${collectionIdOrAlias}/featuredItems`) - // .then(() => undefined) - // .catch((error) => { - // throw error - // }) - // } + public async deleteCollectionFeaturedItems(collectionIdOrAlias: number | string): Promise { + return this.doDelete(`/${this.collectionsResourceName}/${collectionIdOrAlias}/featuredItems`) + .then(() => undefined) + .catch((error) => { + throw error + }) + } } From a18e20ae3e747fbac6429efbbccf23e83ea4d5f6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Germ=C3=A1n=20Saracca?= Date: Mon, 13 Jan 2025 18:03:07 -0300 Subject: [PATCH 10/18] test: fix create different collection alias on functional --- .../functional/collections/GetCollectionFeaturedItems.test.ts | 4 ++-- .../collections/UpdateCollectionFeaturedItems.test.ts | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/test/functional/collections/GetCollectionFeaturedItems.test.ts b/test/functional/collections/GetCollectionFeaturedItems.test.ts index d389d7cd..07483171 100644 --- a/test/functional/collections/GetCollectionFeaturedItems.test.ts +++ b/test/functional/collections/GetCollectionFeaturedItems.test.ts @@ -12,7 +12,7 @@ import { import { ROOT_COLLECTION_ID } from '../../../src/collections/domain/models/Collection' describe('execute', () => { - const testCollectionAlias = 'collectionsRepositoryTestCollection' + const testCollectionAlias = 'getCollectionsFeaturedItemsTest' let testFeaturedItemId: number beforeEach(async () => { @@ -59,7 +59,7 @@ describe('execute', () => { expect(featuredItemsResponse[0].displayOrder).toBe(1) // expect(featuredItemsResponse[0].content).toBe('

Test content

') expect(featuredItemsResponse[0].imageFileUrl).toBe( - 'http://localhost:8080/api/access/dataverseFeatureItemImage/1' + `http://localhost:8080/api/access/dataverseFeatureItemImage/${featuredItemsResponse[0].id}` ) expect(featuredItemsResponse[0].imageFileName).toBe('featured-item-test-image.png') }) diff --git a/test/functional/collections/UpdateCollectionFeaturedItems.test.ts b/test/functional/collections/UpdateCollectionFeaturedItems.test.ts index 22f29005..2829d9fa 100644 --- a/test/functional/collections/UpdateCollectionFeaturedItems.test.ts +++ b/test/functional/collections/UpdateCollectionFeaturedItems.test.ts @@ -19,7 +19,7 @@ import { //TODO:ME - After content sanitization is fixed in backend, check if the content is being updated correctly keeping classes, href, rel, target, etc attributes. describe('execute', () => { - const testCollectionAlias = 'collectionsRepositoryTestCollection' + const testCollectionAlias = 'updateCollectionFeaturedItemsTest' beforeEach(async () => { ApiConfig.init( From 4266785a5eaef19100881a009a5317312757da8f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Germ=C3=A1n=20Saracca?= Date: Tue, 14 Jan 2025 09:37:02 -0300 Subject: [PATCH 11/18] test: apply changes now that backend works correctly --- .../GetCollectionFeaturedItems.test.ts | 4 +- .../UpdateCollectionFeaturedItems.test.ts | 101 ++++++------------ .../collections/CollectionsRepository.test.ts | 8 +- .../collections/collectionHelper.ts | 22 ++++ 4 files changed, 61 insertions(+), 74 deletions(-) diff --git a/test/functional/collections/GetCollectionFeaturedItems.test.ts b/test/functional/collections/GetCollectionFeaturedItems.test.ts index 07483171..48e1bcc1 100644 --- a/test/functional/collections/GetCollectionFeaturedItems.test.ts +++ b/test/functional/collections/GetCollectionFeaturedItems.test.ts @@ -57,7 +57,7 @@ describe('execute', () => { 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].content).toBe('

Test content

') expect(featuredItemsResponse[0].imageFileUrl).toBe( `http://localhost:8080/api/access/dataverseFeatureItemImage/${featuredItemsResponse[0].id}` ) @@ -75,7 +75,7 @@ describe('execute', () => { 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].content).toBe('

Test content

') expect(featuredItemsResponse[1].imageFileUrl).toBeUndefined() expect(featuredItemsResponse[1].imageFileName).toBeUndefined() diff --git a/test/functional/collections/UpdateCollectionFeaturedItems.test.ts b/test/functional/collections/UpdateCollectionFeaturedItems.test.ts index 2829d9fa..ebd37f4b 100644 --- a/test/functional/collections/UpdateCollectionFeaturedItems.test.ts +++ b/test/functional/collections/UpdateCollectionFeaturedItems.test.ts @@ -12,12 +12,12 @@ import { deleteCollectionFeaturedItemViaApi } from '../../testHelpers/collections/collectionFeaturedItemsHelper' import { + CONTENT_FIELD_WITH_ALL_TAGS, createCollectionViaApi, - deleteCollectionViaApi + deleteCollectionViaApi, + EXPECTED_CONTENT_FIELD_WITH_ALL_TAGS } from '../../testHelpers/collections/collectionHelper' -//TODO:ME - After content sanitization is fixed in backend, check if the content is being updated correctly keeping classes, href, rel, target, etc attributes. - describe('execute', () => { const testCollectionAlias = 'updateCollectionFeaturedItemsTest' @@ -64,7 +64,7 @@ describe('execute', () => { keepFile: false }, { - content: '

Test content 3

', + content: CONTENT_FIELD_WITH_ALL_TAGS, displayOrder: 2, file: createImageFile('featured-item-test-image-3.png'), keepFile: false @@ -78,17 +78,17 @@ describe('execute', () => { expect(updatedFeaturedItemsResponse.length).toBe(3) - // expect(updatedFeaturedItemsResponse[0].content).toBe(newFeaturedItems[0].content) + 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(updatedFeaturedItemsResponse[1].content).toBe(newFeaturedItems[1].content) + 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(updatedFeaturedItemsResponse[2].content).toBe(newFeaturedItems[2].content) + 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( @@ -132,63 +132,28 @@ describe('execute', () => { } }) - // TODO:ME - Returning a 500 instead of a proper error message indicating which item property has an error. - // test('should throw an error when featured item content is empty', async () => { - // const newFeaturedItems: CollectionFeaturedItemsDTO = [ - // { - // content: '', - // displayOrder: 0, - // file: undefined, - // keepFile: false - // } - // ] - // let writeError: WriteError | undefined - - // try { - // await updateCollectionFeaturedItems.execute(testCollectionAlias, newFeaturedItems) - // } catch (error) { - // writeError = error - // } finally { - // expect(writeError).toBeInstanceOf(WriteError) - // expect((writeError as WriteError).message).toEqual( - // 'There was an error when writing the resource. Reason was: [400] Content cannot be empty' - // ) - // } - // }) - - // TODO:ME - Currently not throwing an error and saving them with the same display order. - // test('should throw an error when featured items have the same display order', async () => { - // const newFeaturedItems: CollectionFeaturedItemsDTO = [ - // { - // content: '

Test content 1

', - // displayOrder: 0, - // file: undefined, - // keepFile: false - // }, - // { - // content: '

Test content 2

', - // displayOrder: 0, - // file: undefined, - // keepFile: false - // } - // ] - // let writeError: WriteError | undefined - - // try { - // const resp = await updateCollectionFeaturedItems.execute( - // testCollectionAlias, - // newFeaturedItems - // ) - // console.log({ resp }) - // } catch (error) { - // writeError = error - // } finally { - // expect(writeError).toBeInstanceOf(WriteError) - // expect((writeError as WriteError).message).toEqual( - // 'There was an error when writing the resource. Reason was: [400] Display order must be unique' - // ) - // } - // }) + test('should throw an error when featured item content is empty', async () => { + const newFeaturedItems: CollectionFeaturedItemsDTO = [ + { + content: '', + displayOrder: 0, + file: undefined, + keepFile: false + } + ] + let writeError: WriteError | undefined + + try { + await updateCollectionFeaturedItems.execute(testCollectionAlias, newFeaturedItems) + } catch (error) { + writeError = error + } finally { + expect(writeError).toBeInstanceOf(WriteError) + expect((writeError as WriteError).message).toEqual( + "There was an error when writing the resource. Reason was: [400] Featured item 'content' property should be provided and not empty." + ) + } + }) describe('keepFile behaviour', () => { let testFeaturedItemId: number @@ -239,7 +204,7 @@ describe('execute', () => { expect(updatedFeaturedItemsResponse.length).toBe(1) - // expect(updatedFeaturedItemsResponse[0].content).toBe(newFeaturedItems[0].content) + expect(updatedFeaturedItemsResponse[0].content).toBe(newFeaturedItems[0].content) expect(updatedFeaturedItemsResponse[0].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) @@ -266,7 +231,7 @@ describe('execute', () => { expect(updatedFeaturedItemsResponse.length).toBe(1) - // expect(updatedFeaturedItemsResponse[0].content).toBe(newFeaturedItems[0].content) + 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() @@ -290,7 +255,7 @@ describe('execute', () => { expect(updatedFeaturedItemsResponse.length).toBe(1) - // expect(updatedFeaturedItemsResponse[0].content).toBe(newFeaturedItems[0].content) + 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' @@ -318,7 +283,7 @@ describe('execute', () => { expect(updatedFeaturedItemsResponse.length).toBe(1) - // expect(updatedFeaturedItemsResponse[0].content).toBe(newFeaturedItems[0].content) + expect(updatedFeaturedItemsResponse[0].content).toBe(newFeaturedItems[0].content) expect(updatedFeaturedItemsResponse[0].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) diff --git a/test/integration/collections/CollectionsRepository.test.ts b/test/integration/collections/CollectionsRepository.test.ts index eda00c2f..8633b039 100644 --- a/test/integration/collections/CollectionsRepository.test.ts +++ b/test/integration/collections/CollectionsRepository.test.ts @@ -924,7 +924,7 @@ describe('CollectionsRepository', () => { 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].content).toBe('

Test content

') expect(featuredItemsResponse[0].imageFileUrl).toBe( `http://localhost:8080/api/access/dataverseFeatureItemImage/${featuredItemsResponse[0].id}` ) @@ -983,17 +983,17 @@ describe('CollectionsRepository', () => { expect(response).toHaveLength(3) - // expect(response[0].content).toEqual(newFeaturedItems[0].content) + 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].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].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( diff --git a/test/testHelpers/collections/collectionHelper.ts b/test/testHelpers/collections/collectionHelper.ts index 65de5d4a..6561329c 100644 --- a/test/testHelpers/collections/collectionHelper.ts +++ b/test/testHelpers/collections/collectionHelper.ts @@ -207,3 +207,25 @@ export const createCollectionFacetRequestPayload = (): CollectionFacetPayload => displayName: 'testDisplayName' } } + +export const CONTENT_FIELD_WITH_ALL_TAGS = + '

A title

Esto es una oracion que contiene texto en negrita, italica, subrayada, tachado, de tipo code, este es un link que apunta a youtube.

Una lista desordenada:

  • Item

  • Item

Una lista ordenada:

  1. Item 1

  2. Item 2

Este es un blockquote.

Esto que viene es un bloque de codigo.

      <Controller name={`featuredItems.${itemIndex}.content`} control={control} rules={rules} render={({ field: { onChange, ref, value }, fieldState: { invalid, error } }) => { console.log({ value }) return ( <Col> <RichTextEditor initialValue={value as string} editorContentAriaLabelledBy={`featuredItems.${itemIndex}.content`} onChange={onChange} invalid={invalid} ariaRequired ref={ref} /> {invalid && <div className={styles["error-msg"]}>{error?.message}</div>} </Col> ) }} />
' + +export const EXPECTED_CONTENT_FIELD_WITH_ALL_TAGS = + '

A title

\n' + + '

Esto es una oracion que contiene texto en negrita, italica, subrayada, tachado, de tipo code, este es un link que apunta a youtube.

\n' + + '

Una lista desordenada:

\n' + + '
    \n' + + '
  • Item

  • \n' + + '
  • Item

  • \n' + + '
\n' + + '

Una lista ordenada:

\n' + + '
    \n' + + '
  1. Item 1

  2. \n' + + '
  3. Item 2

  4. \n' + + '
\n' + + '
\n' + + '

Este es un blockquote.

\n' + + '
\n' + + '

Esto que viene es un bloque de codigo.

\n' + + '
      <Controller name={`featuredItems.${itemIndex}.content`} control={control} rules={rules} render={({ field: { onChange, ref, value }, fieldState: { invalid, error } }) => { console.log({ value }) return ( <Col> <RichTextEditor initialValue={value as string} editorContentAriaLabelledBy={`featuredItems.${itemIndex}.content`} onChange={onChange} invalid={invalid} ariaRequired ref={ref} /> {invalid && <div className={styles["error-msg"]}>{error?.message}</div>} </Col> ) }} />
' From 3400f2746d337e326698361d8e606cf97b9f0bb8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Germ=C3=A1n=20Saracca?= Date: Tue, 14 Jan 2025 10:01:21 -0300 Subject: [PATCH 12/18] feat: use case and docs --- docs/useCases.md | 23 +++++++++++++++++++ .../useCases/DeleteCollectionFeaturedItems.ts | 23 +++++++++++++++++++ src/collections/index.ts | 5 +++- 3 files changed, 50 insertions(+), 1 deletion(-) create mode 100644 src/collections/domain/useCases/DeleteCollectionFeaturedItems.ts diff --git a/docs/useCases.md b/docs/useCases.md index 92cd9222..26317d1c 100644 --- a/docs/useCases.md +++ b/docs/useCases.md @@ -20,6 +20,7 @@ The different use cases currently available in the package are classified below, - [Update a Collection](#update-a-collection) - [Publish a Collection](#publish-a-collection) - [Update Collection Featured Items](#update-collection-featured-items) + - [Delete Collection Featured Items](#delete-collection-featured-items) - [Datasets](#Datasets) - [Datasets read use cases](#datasets-read-use-cases) - [Get a Dataset](#get-a-dataset) @@ -340,6 +341,28 @@ The `collectionIdOrAlias` is a generic collection identifier, which can be eithe _See [use case](../src/collections/domain/useCases/UpdateCollectionFeaturedItems.ts)_ definition. +#### Delete Collection Featured Items + +Deletes all featured items from a collection, given a collection identifier. + +##### Example call: + +```typescript +import { deleteCollectionFeaturedItems } from '@iqss/dataverse-client-javascript' + +/* ... */ + +const collectionIdOrAlias = 12345 + +deleteCollectionFeaturedItems.execute(collectionIdOrAlias) + +/* ... */ +``` + +The `collectionIdOrAlias` is a generic collection identifier, which can be either a string (for queries by CollectionAlias), or a number (for queries by CollectionId). + +_See [use case](../src/collections/domain/useCases/DeleteCollectionFeaturedItems.ts)_ definition. + ## Datasets ### Datasets Read Use Cases diff --git a/src/collections/domain/useCases/DeleteCollectionFeaturedItems.ts b/src/collections/domain/useCases/DeleteCollectionFeaturedItems.ts new file mode 100644 index 00000000..d8c84ce6 --- /dev/null +++ b/src/collections/domain/useCases/DeleteCollectionFeaturedItems.ts @@ -0,0 +1,23 @@ +import { UseCase } from '../../../core/domain/useCases/UseCase' +import { ROOT_COLLECTION_ID } from '../models/Collection' +import { ICollectionsRepository } from '../repositories/ICollectionsRepository' + +export class DeleteCollectionFeaturedItems implements UseCase { + private collectionsRepository: ICollectionsRepository + + constructor(collectionsRepository: ICollectionsRepository) { + this.collectionsRepository = collectionsRepository + } + + /** + * Deletes all featured items from a collection, given a collection identifier. + * + * @param {number | string} [collectionIdOrAlias = ':root'] - A generic collection identifier, which can be either a string (for queries by CollectionAlias), or a number (for queries by CollectionId) + * If this parameter is not set, the default value is: ':root' + * @returns {Promise} - This method does not return anything upon successful completion. + * @throws {WriteError} - If there are errors while writing data. + */ + async execute(collectionIdOrAlias: number | string = ROOT_COLLECTION_ID): Promise { + return await this.collectionsRepository.deleteCollectionFeaturedItems(collectionIdOrAlias) + } +} diff --git a/src/collections/index.ts b/src/collections/index.ts index 99721a7d..e3a50dce 100644 --- a/src/collections/index.ts +++ b/src/collections/index.ts @@ -8,6 +8,7 @@ import { UpdateCollection } from './domain/useCases/UpdateCollection' import { GetCollectionFeaturedItems } from './domain/useCases/GetCollectionFeaturedItems' import { CollectionsRepository } from './infra/repositories/CollectionsRepository' import { UpdateCollectionFeaturedItems } from './domain/useCases/UpdateCollectionFeaturedItems' +import { DeleteCollectionFeaturedItems } from './domain/useCases/DeleteCollectionFeaturedItems' const collectionsRepository = new CollectionsRepository() @@ -20,6 +21,7 @@ const publishCollection = new PublishCollection(collectionsRepository) const updateCollection = new UpdateCollection(collectionsRepository) const getCollectionFeaturedItems = new GetCollectionFeaturedItems(collectionsRepository) const updateCollectionFeaturedItems = new UpdateCollectionFeaturedItems(collectionsRepository) +const deleteCollectionFeaturedItems = new DeleteCollectionFeaturedItems(collectionsRepository) export { getCollection, @@ -30,7 +32,8 @@ export { publishCollection, updateCollection, getCollectionFeaturedItems, - updateCollectionFeaturedItems + updateCollectionFeaturedItems, + deleteCollectionFeaturedItems } export { Collection, CollectionInputLevel } from './domain/models/Collection' export { CollectionFacet } from './domain/models/CollectionFacet' From 637977cc4c87024909ebfc3052651742573fbf2a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Germ=C3=A1n=20Saracca?= Date: Tue, 14 Jan 2025 10:34:24 -0300 Subject: [PATCH 13/18] tests for delete collection featured items --- .../DeleteCollectionFeaturedItems.test.ts | 82 ++++++++++++++++ .../collections/CollectionsRepository.test.ts | 98 ++++++++++--------- .../collectionFeaturedItemsHelper.ts | 2 +- .../DeleteCollectionFeaturedItems.test.ts | 30 ++++++ 4 files changed, 167 insertions(+), 45 deletions(-) create mode 100644 test/functional/collections/DeleteCollectionFeaturedItems.test.ts create mode 100644 test/unit/collections/DeleteCollectionFeaturedItems.test.ts diff --git a/test/functional/collections/DeleteCollectionFeaturedItems.test.ts b/test/functional/collections/DeleteCollectionFeaturedItems.test.ts new file mode 100644 index 00000000..55b6ba41 --- /dev/null +++ b/test/functional/collections/DeleteCollectionFeaturedItems.test.ts @@ -0,0 +1,82 @@ +import { ApiConfig, deleteCollectionFeaturedItems, WriteError } from '../../../src' +import { TestConstants } from '../../testHelpers/TestConstants' +import { DataverseApiAuthMechanism } from '../../../src/core/infra/repositories/ApiConfig' +import { + createCollectionViaApi, + deleteCollectionViaApi +} from '../../testHelpers/collections/collectionHelper' +import { + createCollectionFeaturedItemViaApi, + deleteCollectionFeaturedItemsViaApi +} from '../../testHelpers/collections/collectionFeaturedItemsHelper' + +describe('execute', () => { + const testCollectionAlias = 'deleteCollectionFeaturedItemsTest' + + beforeEach(async () => { + ApiConfig.init( + TestConstants.TEST_API_URL, + DataverseApiAuthMechanism.API_KEY, + process.env.TEST_API_KEY + ) + }) + + beforeAll(async () => { + try { + await createCollectionViaApi(testCollectionAlias) + await createCollectionFeaturedItemViaApi(testCollectionAlias, { + content: '

Test content

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

Test content 2

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

Test content 3

', + displayOrder: 3, + withFile: false + }) + } catch (error) { + throw new Error( + `Tests beforeAll(): Error while creating test collection: ${testCollectionAlias}` + ) + } + }) + + afterAll(async () => { + try { + await deleteCollectionFeaturedItemsViaApi(testCollectionAlias) + await deleteCollectionViaApi(testCollectionAlias) + } catch (error) { + throw new Error( + `Tests afterAll(): Error while deleting test collection: ${testCollectionAlias}` + ) + } + }) + + test('should succesfully delete all featured items from a collection', async () => { + const actual = await deleteCollectionFeaturedItems.execute(testCollectionAlias) + + expect(actual).toBeUndefined() + }) + + test('should throw an error when collection does not exist', async () => { + const invalidCollectionAlias = 'invalid-collection-alias' + let writeError: WriteError | undefined + + try { + await deleteCollectionFeaturedItems.execute(invalidCollectionAlias) + } catch (error) { + writeError = error + } finally { + expect(writeError).toBeInstanceOf(WriteError) + expect((writeError as WriteError).message).toEqual( + `There was an error when writing the resource. Reason was: [404] Can't find dataverse with identifier='${invalidCollectionAlias}'` + ) + } + }) +}) diff --git a/test/integration/collections/CollectionsRepository.test.ts b/test/integration/collections/CollectionsRepository.test.ts index 8633b039..65a9eadd 100644 --- a/test/integration/collections/CollectionsRepository.test.ts +++ b/test/integration/collections/CollectionsRepository.test.ts @@ -37,7 +37,7 @@ import { ROOT_COLLECTION_ID } from '../../../src/collections/domain/models/Colle import { createCollectionFeaturedItemViaApi, createImageFile, - deleteAllCollectionFeaturedItemsViaApi, + deleteCollectionFeaturedItemsViaApi, deleteCollectionFeaturedItemViaApi } from '../../testHelpers/collections/collectionFeaturedItemsHelper' @@ -946,7 +946,7 @@ describe('CollectionsRepository', () => { describe('updateCollectionFeaturedItems', () => { afterAll(async () => { try { - await deleteAllCollectionFeaturedItemsViaApi(testCollectionAlias) + await deleteCollectionFeaturedItemsViaApi(testCollectionAlias) } catch (error) { throw new Error( `Tests afterAll(): Error while deleting all featured items from collection: ${testCollectionAlias}` @@ -1002,46 +1002,56 @@ describe('CollectionsRepository', () => { }) }) - // describe('deleteAllCollectionFeaturedItems', () => { - - // beforeAll(async () => { - // try { - // await createCollectionFeaturedItemViaApi(testCollectionAlias, { - // content: '

Test content

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

Test content 2

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

Test content 3

', - // displayOrder: 3, - // withFile: false - // }) - - // } catch (error) { - // throw new Error( - // `Tests afterAll(): Error while deleting all featured items from collection: ${testCollectionAlias}` - // ) - // } - // }) - - // it('should delete all collection featured items', async () => { - // const featuredItemsResponseFirst = await sut.getCollectionFeaturedItems(testCollectionAlias) - - // console.log({ featuredItemsResponseFirst }) - - // await deleteAllCollectionFeaturedItemsViaApi(testCollectionAlias) - - // const featuredItemsResponse = await sut.getCollectionFeaturedItems(testCollectionAlias) - - // console.log({ featuredItemsResponse }) - - // expect(featuredItemsResponse).toStrictEqual([]) - // }) - // }) + describe('deleteCollectionFeaturedItems', () => { + beforeAll(async () => { + try { + await createCollectionFeaturedItemViaApi(testCollectionAlias, { + content: '

Test content

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

Test content 2

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

Test content 3

', + displayOrder: 3, + withFile: false + }) + } catch (error) { + throw new Error( + `Tests afterAll(): Error while creating test featured items for collection: ${testCollectionAlias}` + ) + } + }) + + afterAll(async () => { + try { + await deleteCollectionFeaturedItemsViaApi(testCollectionAlias) + } catch (error) { + throw new Error( + `Tests afterAll(): Error while deleting test collection featured items: ${testCollectionAlias}` + ) + } + }) + + it('should delete all collection featured items', async () => { + const featuredItemsResponseBeforeDeletion = await sut.getCollectionFeaturedItems( + testCollectionAlias + ) + + expect(featuredItemsResponseBeforeDeletion).toHaveLength(3) + + await sut.deleteCollectionFeaturedItems(testCollectionAlias) + + const featuredItemsResponseAfterDeletion = await sut.getCollectionFeaturedItems( + testCollectionAlias + ) + + expect(featuredItemsResponseAfterDeletion).toStrictEqual([]) + }) + }) }) diff --git a/test/testHelpers/collections/collectionFeaturedItemsHelper.ts b/test/testHelpers/collections/collectionFeaturedItemsHelper.ts index 8a4b5d40..d05cb872 100644 --- a/test/testHelpers/collections/collectionFeaturedItemsHelper.ts +++ b/test/testHelpers/collections/collectionFeaturedItemsHelper.ts @@ -69,7 +69,7 @@ export async function deleteCollectionFeaturedItemViaApi(featuredItemId: number) } } -export async function deleteAllCollectionFeaturedItemsViaApi(collectionAlias: string) { +export async function deleteCollectionFeaturedItemsViaApi(collectionAlias: string) { try { return await axios.delete( `${TestConstants.TEST_API_URL}/dataverses/${collectionAlias}/featuredItems`, diff --git a/test/unit/collections/DeleteCollectionFeaturedItems.test.ts b/test/unit/collections/DeleteCollectionFeaturedItems.test.ts new file mode 100644 index 00000000..fb7c2be9 --- /dev/null +++ b/test/unit/collections/DeleteCollectionFeaturedItems.test.ts @@ -0,0 +1,30 @@ +import { ICollectionsRepository } from '../../../src/collections/domain/repositories/ICollectionsRepository' +import { WriteError } from '../../../src' +import { DeleteCollectionFeaturedItems } from '../../../src/collections/domain/useCases/DeleteCollectionFeaturedItems' + +describe('execute', () => { + test('should return undefined on repository success', async () => { + const collectionRepositoryStub: ICollectionsRepository = {} as ICollectionsRepository + collectionRepositoryStub.deleteCollectionFeaturedItems = jest.fn().mockResolvedValue(undefined) + + const testDeleteCollectionFeaturedItems = new DeleteCollectionFeaturedItems( + collectionRepositoryStub + ) + + const actual = await testDeleteCollectionFeaturedItems.execute(1) + + expect(actual).toEqual(undefined) + }) + + test('should return error result on repository error', async () => { + const collectionRepositoryStub: ICollectionsRepository = {} as ICollectionsRepository + collectionRepositoryStub.deleteCollectionFeaturedItems = jest + .fn() + .mockRejectedValue(new WriteError()) + const testDeleteCollectionFeaturedItems = new DeleteCollectionFeaturedItems( + collectionRepositoryStub + ) + + await expect(testDeleteCollectionFeaturedItems.execute(1)).rejects.toThrow(WriteError) + }) +}) From 8db395275d78c57cc894f557c8b5c180c9a1ad24 Mon Sep 17 00:00:00 2001 From: Cheng Shi Date: Wed, 22 Jan 2025 09:50:58 -0500 Subject: [PATCH 14/18] chore: minor changes on format and extra comments --- .../infra/repositories/CollectionsRepository.ts | 2 -- .../collections/collectionFeaturedItemsHelper.ts | 7 ------- .../unit/collections/DeleteCollectionFeaturedItems.test.ts | 1 - .../unit/collections/UpdateCollectionFeaturedItems.test.ts | 1 - 4 files changed, 11 deletions(-) diff --git a/src/collections/infra/repositories/CollectionsRepository.ts b/src/collections/infra/repositories/CollectionsRepository.ts index 3c819b7d..9309036a 100644 --- a/src/collections/infra/repositories/CollectionsRepository.ts +++ b/src/collections/infra/repositories/CollectionsRepository.ts @@ -22,8 +22,6 @@ import { transformCollectionFeaturedItemsPayloadToCollectionFeaturedItems } from import { CollectionFeaturedItemsDTO } from '../../domain/dtos/CollectionFeaturedItemsDTO' import { ApiConstants } from '../../../core/infra/repositories/ApiConstants' -// TODO:ME - Create Delete All Featured Items use case - export interface NewCollectionRequestPayload { alias: string name: string diff --git a/test/testHelpers/collections/collectionFeaturedItemsHelper.ts b/test/testHelpers/collections/collectionFeaturedItemsHelper.ts index d05cb872..12290cca 100644 --- a/test/testHelpers/collections/collectionFeaturedItemsHelper.ts +++ b/test/testHelpers/collections/collectionFeaturedItemsHelper.ts @@ -44,7 +44,6 @@ export async function createCollectionFeaturedItemViaApi( } }) .then((response) => { - // console.log({ singleFeatItemCreated: JSON.stringify(response.data.data) }) return response.data.data }) } catch (error) { @@ -61,9 +60,6 @@ export async function deleteCollectionFeaturedItemViaApi(featuredItemId: number) headers: { 'Content-Type': 'application/json', 'X-Dataverse-Key': process.env.TEST_API_KEY } } ) - // .then((resp) => { - // console.log({ deletedResp: JSON.stringify(resp.data) }) - // }) } catch (error) { throw new Error(`Error while deleting collection featured item with id ${featuredItemId}`) } @@ -77,9 +73,6 @@ export async function deleteCollectionFeaturedItemsViaApi(collectionAlias: strin headers: { 'Content-Type': 'application/json', 'X-Dataverse-Key': process.env.TEST_API_KEY } } ) - // .then((resp) => { - // console.log({ deletedAllResp: JSON.stringify(resp.data) }) - // }) } catch (error) { console.log(error) throw new Error(`Error while deleting all featured items from collection: ${collectionAlias}`) diff --git a/test/unit/collections/DeleteCollectionFeaturedItems.test.ts b/test/unit/collections/DeleteCollectionFeaturedItems.test.ts index fb7c2be9..4bff6aba 100644 --- a/test/unit/collections/DeleteCollectionFeaturedItems.test.ts +++ b/test/unit/collections/DeleteCollectionFeaturedItems.test.ts @@ -6,7 +6,6 @@ describe('execute', () => { test('should return undefined on repository success', async () => { const collectionRepositoryStub: ICollectionsRepository = {} as ICollectionsRepository collectionRepositoryStub.deleteCollectionFeaturedItems = jest.fn().mockResolvedValue(undefined) - const testDeleteCollectionFeaturedItems = new DeleteCollectionFeaturedItems( collectionRepositoryStub ) diff --git a/test/unit/collections/UpdateCollectionFeaturedItems.test.ts b/test/unit/collections/UpdateCollectionFeaturedItems.test.ts index 02e81a0d..c21fb240 100644 --- a/test/unit/collections/UpdateCollectionFeaturedItems.test.ts +++ b/test/unit/collections/UpdateCollectionFeaturedItems.test.ts @@ -16,7 +16,6 @@ describe('execute', () => { collectionRepositoryStub.updateCollectionFeaturedItems = jest .fn() .mockResolvedValue(testFeaturedItems) - const testGetCollectionFeaturedItems = new UpdateCollectionFeaturedItems( collectionRepositoryStub ) From b01c7b026a88721b6f69454afb58b69c1e87a446 Mon Sep 17 00:00:00 2001 From: Cheng Shi Date: Wed, 22 Jan 2025 11:17:39 -0500 Subject: [PATCH 15/18] fix: typo --- test/testHelpers/collections/collectionFeaturedItemsHelper.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/testHelpers/collections/collectionFeaturedItemsHelper.ts b/test/testHelpers/collections/collectionFeaturedItemsHelper.ts index 12290cca..d188ada5 100644 --- a/test/testHelpers/collections/collectionFeaturedItemsHelper.ts +++ b/test/testHelpers/collections/collectionFeaturedItemsHelper.ts @@ -37,7 +37,7 @@ export async function createCollectionFeaturedItemViaApi( } return await axios - .post(`${TestConstants.TEST_API_URL}/dataverses/${collectionAlias}/featuredItem`, formData, { + .post(`${TestConstants.TEST_API_URL}/dataverses/${collectionAlias}/featuredItems`, formData, { headers: { 'Content-Type': 'multipart/form-data', 'X-Dataverse-Key': process.env.TEST_API_KEY From b6608d9082c356471dda28fe55eeaa38827f9a62 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Germ=C3=A1n=20Saracca?= Date: Mon, 3 Feb 2025 08:32:29 -0300 Subject: [PATCH 16/18] chore: test env back to unstable --- test/environment/.env | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/environment/.env b/test/environment/.env index bf08e36f..80e9a14e 100644 --- a/test/environment/.env +++ b/test/environment/.env @@ -1,6 +1,6 @@ POSTGRES_VERSION=13 DATAVERSE_DB_USER=dataverse SOLR_VERSION=9.3.0 -DATAVERSE_IMAGE_REGISTRY=ghcr.io -DATAVERSE_IMAGE_TAG=10943-featured-items +DATAVERSE_IMAGE_REGISTRY=docker.io +DATAVERSE_IMAGE_TAG=unstable DATAVERSE_BOOTSTRAP_TIMEOUT=5m From 57e8e43f4b1fe5a9a0df07f31a46225f85224c34 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Germ=C3=A1n=20Saracca?= Date: Mon, 3 Feb 2025 08:52:39 -0300 Subject: [PATCH 17/18] fix ts error --- .../transformers/collectionFeaturedItemsTransformer.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/collections/infra/repositories/transformers/collectionFeaturedItemsTransformer.ts b/src/collections/infra/repositories/transformers/collectionFeaturedItemsTransformer.ts index 3428f6be..f3bfb135 100644 --- a/src/collections/infra/repositories/transformers/collectionFeaturedItemsTransformer.ts +++ b/src/collections/infra/repositories/transformers/collectionFeaturedItemsTransformer.ts @@ -8,8 +8,8 @@ export const transformCollectionFeaturedItemsPayloadToCollectionFeaturedItems = .map((collectionFeaturedItemPayload) => ({ id: collectionFeaturedItemPayload.id, content: collectionFeaturedItemPayload.content, - imageFileUrl: collectionFeaturedItemPayload.imageFileUrl, - imageFileName: collectionFeaturedItemPayload.imageFileName, + imageFileUrl: collectionFeaturedItemPayload.imageFileUrl || undefined, + imageFileName: collectionFeaturedItemPayload.imageFileName || undefined, displayOrder: collectionFeaturedItemPayload.displayOrder })) .sort((a, b) => a.displayOrder - b.displayOrder) From 3439d083b059ab30d60d0f22b6a0750948cb78c4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Germ=C3=A1n=20Saracca?= Date: Mon, 3 Feb 2025 08:53:07 -0300 Subject: [PATCH 18/18] test: update due to ts errors and image url --- .../DeleteCollectionFeaturedItems.test.ts | 2 +- .../collections/GetCollectionFeaturedItems.test.ts | 4 ++-- .../UpdateCollectionFeaturedItems.test.ts | 12 ++++++------ .../collections/CollectionsRepository.test.ts | 4 ++-- .../collections/collectionFeaturedItemsHelper.ts | 2 +- 5 files changed, 12 insertions(+), 12 deletions(-) diff --git a/test/functional/collections/DeleteCollectionFeaturedItems.test.ts b/test/functional/collections/DeleteCollectionFeaturedItems.test.ts index 55b6ba41..3f66a0e8 100644 --- a/test/functional/collections/DeleteCollectionFeaturedItems.test.ts +++ b/test/functional/collections/DeleteCollectionFeaturedItems.test.ts @@ -71,7 +71,7 @@ describe('execute', () => { try { await deleteCollectionFeaturedItems.execute(invalidCollectionAlias) } catch (error) { - writeError = error + writeError = error as WriteError } finally { expect(writeError).toBeInstanceOf(WriteError) expect((writeError as WriteError).message).toEqual( diff --git a/test/functional/collections/GetCollectionFeaturedItems.test.ts b/test/functional/collections/GetCollectionFeaturedItems.test.ts index 48e1bcc1..a2f75a70 100644 --- a/test/functional/collections/GetCollectionFeaturedItems.test.ts +++ b/test/functional/collections/GetCollectionFeaturedItems.test.ts @@ -59,7 +59,7 @@ describe('execute', () => { expect(featuredItemsResponse[0].displayOrder).toBe(1) expect(featuredItemsResponse[0].content).toBe('

Test content

') expect(featuredItemsResponse[0].imageFileUrl).toBe( - `http://localhost:8080/api/access/dataverseFeatureItemImage/${featuredItemsResponse[0].id}` + `http://localhost:8080/api/access/dataverseFeaturedItemImage/${featuredItemsResponse[0].id}` ) expect(featuredItemsResponse[0].imageFileName).toBe('featured-item-test-image.png') }) @@ -95,7 +95,7 @@ describe('execute', () => { try { await getCollectionFeaturedItems.execute(invalidCollectionAlias) } catch (error) { - readError = error + readError = error as ReadError } finally { expect(readError).toBeInstanceOf(ReadError) expect((readError as ReadError).message).toEqual( diff --git a/test/functional/collections/UpdateCollectionFeaturedItems.test.ts b/test/functional/collections/UpdateCollectionFeaturedItems.test.ts index ebd37f4b..2c196c63 100644 --- a/test/functional/collections/UpdateCollectionFeaturedItems.test.ts +++ b/test/functional/collections/UpdateCollectionFeaturedItems.test.ts @@ -92,7 +92,7 @@ describe('execute', () => { expect(updatedFeaturedItemsResponse[2].displayOrder).toBe(newFeaturedItems[2].displayOrder) expect(updatedFeaturedItemsResponse[2].imageFileName).toEqual('featured-item-test-image-3.png') expect(updatedFeaturedItemsResponse[2].imageFileUrl).toBe( - `http://localhost:8080/api/access/dataverseFeatureItemImage/${updatedFeaturedItemsResponse[2].id}` + `http://localhost:8080/api/access/dataverseFeaturedItemImage/${updatedFeaturedItemsResponse[2].id}` ) }) @@ -123,7 +123,7 @@ describe('execute', () => { try { await updateCollectionFeaturedItems.execute(invalidCollectionAlias, newFeaturedItems) } catch (error) { - writeError = error + writeError = error as WriteError } finally { expect(writeError).toBeInstanceOf(WriteError) expect((writeError as WriteError).message).toEqual( @@ -146,7 +146,7 @@ describe('execute', () => { try { await updateCollectionFeaturedItems.execute(testCollectionAlias, newFeaturedItems) } catch (error) { - writeError = error + writeError = error as WriteError } finally { expect(writeError).toBeInstanceOf(WriteError) expect((writeError as WriteError).message).toEqual( @@ -209,7 +209,7 @@ describe('execute', () => { // 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/dataverseFeatureItemImage/${updatedFeaturedItemsResponse[0].id}` + `http://localhost:8080/api/access/dataverseFeaturedItemImage/${updatedFeaturedItemsResponse[0].id}` ) }) @@ -261,7 +261,7 @@ describe('execute', () => { 'featured-item-test-image-updated.png' ) expect(updatedFeaturedItemsResponse[0].imageFileUrl).toBe( - `http://localhost:8080/api/access/dataverseFeatureItemImage/${updatedFeaturedItemsResponse[0].id}` + `http://localhost:8080/api/access/dataverseFeaturedItemImage/${updatedFeaturedItemsResponse[0].id}` ) }) @@ -288,7 +288,7 @@ describe('execute', () => { // Should keep the existing file even if a file was provided because keepFile is true expect(updatedFeaturedItemsResponse[0].imageFileName).toEqual(testFeaturedItemFilename) expect(updatedFeaturedItemsResponse[0].imageFileUrl).toBe( - `http://localhost:8080/api/access/dataverseFeatureItemImage/${updatedFeaturedItemsResponse[0].id}` + `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 65a9eadd..2a7c9c9d 100644 --- a/test/integration/collections/CollectionsRepository.test.ts +++ b/test/integration/collections/CollectionsRepository.test.ts @@ -926,7 +926,7 @@ describe('CollectionsRepository', () => { expect(featuredItemsResponse[0].displayOrder).toBe(1) expect(featuredItemsResponse[0].content).toBe('

Test content

') expect(featuredItemsResponse[0].imageFileUrl).toBe( - `http://localhost:8080/api/access/dataverseFeatureItemImage/${featuredItemsResponse[0].id}` + `http://localhost:8080/api/access/dataverseFeaturedItemImage/${featuredItemsResponse[0].id}` ) expect(featuredItemsResponse[0].imageFileName).toBe('featured-item-test-image.png') }) @@ -997,7 +997,7 @@ describe('CollectionsRepository', () => { 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/dataverseFeatureItemImage/${response[2].id}` + `http://localhost:8080/api/access/dataverseFeaturedItemImage/${response[2].id}` ) }) }) diff --git a/test/testHelpers/collections/collectionFeaturedItemsHelper.ts b/test/testHelpers/collections/collectionFeaturedItemsHelper.ts index d188ada5..48032389 100644 --- a/test/testHelpers/collections/collectionFeaturedItemsHelper.ts +++ b/test/testHelpers/collections/collectionFeaturedItemsHelper.ts @@ -86,7 +86,7 @@ export const createCollectionFeaturedItemsModel = (): CollectionFeaturedItem[] = content: 'This is a featured item', displayOrder: 1, imageFileName: 'test-image.png', - imageFileUrl: 'http://localhost:8080/api/access/dataverseFeatureItemImage/1' + imageFileUrl: 'http://localhost:8080/api/access/dataverseFeaturedItemImage/1' }, { id: 2,