Skip to content
Merged
Show file tree
Hide file tree
Changes from 9 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 21 additions & 0 deletions docs/useCases.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ The different use cases currently available in the package are classified below,
- [Delete a Collection](#delete-a-collection)
- [Update Collection Featured Items](#update-collection-featured-items)
- [Delete Collection Featured Items](#delete-collection-featured-items)
- [Delete a Collection Featured Item](#delete-a-collection-featured-item)
- [Datasets](#Datasets)
- [Datasets read use cases](#datasets-read-use-cases)
- [Get a Dataset](#get-a-dataset)
Expand Down Expand Up @@ -451,6 +452,26 @@ The `collectionIdOrAlias` is a generic collection identifier, which can be eithe

_See [use case](../src/collections/domain/useCases/DeleteCollectionFeaturedItems.ts)_ definition.

#### Delete A Collection Featured Item

Deletes a single featured item, given a featured item id.

##### Example call:

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

/* ... */

const featuredItemId = 12345

deleteCollectionFeaturedItem.execute(featuredItemId)

/* ... */
```

_See [use case](../src/collections/domain/useCases/DeleteCollectionFeaturedItem.ts)_ definition.

## Datasets

### Datasets Read Use Cases
Expand Down
14 changes: 12 additions & 2 deletions src/collections/domain/dtos/CollectionFeaturedItemsDTO.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,19 @@
export type CollectionFeaturedItemsDTO = CollectionFeaturedItemDTO[]
import { CustomFeaturedItem, DvObjectFeaturedItem } from '../models/CollectionFeaturedItem'

export interface CollectionFeaturedItemDTO {
export type CollectionFeaturedItemsDTO = (CustomFeaturedItemDTO | DvObjectFeaturedItemDTO)[]

export interface CustomFeaturedItemDTO {
id?: number
type: CustomFeaturedItem['type']
content: string
displayOrder: number
file?: File
keepFile: boolean
}

export interface DvObjectFeaturedItemDTO {
id?: number
type: DvObjectFeaturedItem['type']
dvObjectIdentifier: string
displayOrder: number
}
20 changes: 19 additions & 1 deletion src/collections/domain/models/CollectionFeaturedItem.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,25 @@
export interface CollectionFeaturedItem {
export type CollectionFeaturedItem = CustomFeaturedItem | DvObjectFeaturedItem
Comment thread
g-saracca marked this conversation as resolved.
Outdated

export interface CustomFeaturedItem {
id: number
type: FeaturedItemType.CUSTOM
content: string
imageFileName?: string
imageFileUrl?: string
displayOrder: number
}

export interface DvObjectFeaturedItem {
id: number
type: FeaturedItemType.COLLECTION | FeaturedItemType.DATASET | FeaturedItemType.FILE
dvObjectIdentifier: string
dvObjectDisplayName: string
displayOrder: number
}

export enum FeaturedItemType {
CUSTOM = 'custom',
COLLECTION = 'collection',
DATASET = 'dataset',
FILE = 'file'
}
Original file line number Diff line number Diff line change
Expand Up @@ -50,4 +50,5 @@ export interface ICollectionsRepository {
featuredItemDTOs: CollectionFeaturedItemsDTO
): Promise<CollectionFeaturedItem[]>
deleteCollectionFeaturedItems(collectionIdOrAlias: number | string): Promise<void>
deleteCollectionFeaturedItem(featuredItemId: number): Promise<void>
}
21 changes: 21 additions & 0 deletions src/collections/domain/useCases/DeleteCollectionFeaturedItem.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { UseCase } from '../../../core/domain/useCases/UseCase'
import { ICollectionsRepository } from '../repositories/ICollectionsRepository'

export class DeleteCollectionFeaturedItem implements UseCase<void> {
private collectionsRepository: ICollectionsRepository

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

/**
* Deletes a single featured item, given a featured item id.
*
* @param {number} [featuredItemId] - The id of the featured item to delete.
* @returns {Promise<void>} - This method does not return anything upon successful completion.
* @throws {WriteError} - If there are errors while writing data.
*/
async execute(featuredItemId: number): Promise<void> {
return await this.collectionsRepository.deleteCollectionFeaturedItem(featuredItemId)
}
}
5 changes: 4 additions & 1 deletion src/collections/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import { UpdateCollectionFeaturedItems } from './domain/useCases/UpdateCollectio
import { DeleteCollectionFeaturedItems } from './domain/useCases/DeleteCollectionFeaturedItems'
import { DeleteCollection } from './domain/useCases/DeleteCollection'
import { GetMyDataCollectionItems } from './domain/useCases/GetMyDataCollectionItems'
import { DeleteCollectionFeaturedItem } from './domain/useCases/DeleteCollectionFeaturedItem'

const collectionsRepository = new CollectionsRepository()

Expand All @@ -26,6 +27,7 @@ const getCollectionFeaturedItems = new GetCollectionFeaturedItems(collectionsRep
const updateCollectionFeaturedItems = new UpdateCollectionFeaturedItems(collectionsRepository)
const deleteCollectionFeaturedItems = new DeleteCollectionFeaturedItems(collectionsRepository)
const deleteCollection = new DeleteCollection(collectionsRepository)
const deleteCollectionFeaturedItem = new DeleteCollectionFeaturedItem(collectionsRepository)

export {
getCollection,
Expand All @@ -39,7 +41,8 @@ export {
getCollectionFeaturedItems,
updateCollectionFeaturedItems,
deleteCollectionFeaturedItems,
deleteCollection
deleteCollection,
deleteCollectionFeaturedItem
}
export { Collection, CollectionInputLevel } from './domain/models/Collection'
export { CollectionFacet } from './domain/models/CollectionFacet'
Expand Down
61 changes: 47 additions & 14 deletions src/collections/infra/repositories/CollectionsRepository.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,20 @@ import {
SortType
} from '../../domain/models/CollectionSearchCriteria'
import { CollectionItemType } from '../../domain/models/CollectionItemType'
import { CollectionFeaturedItem } from '../../domain/models/CollectionFeaturedItem'
import { transformCollectionFeaturedItemsPayloadToCollectionFeaturedItems } from './transformers/collectionFeaturedItemsTransformer'
import { CollectionFeaturedItemsDTO } from '../../domain/dtos/CollectionFeaturedItemsDTO'
import {
CollectionFeaturedItem,
DvObjectFeaturedItem,
FeaturedItemType
} from '../../domain/models/CollectionFeaturedItem'
import {
domainTypeToApiType,
transformCollectionFeaturedItemsPayloadToCollectionFeaturedItems
} from './transformers/collectionFeaturedItemsTransformer'
import {
CollectionFeaturedItemsDTO,
CustomFeaturedItemDTO,
DvObjectFeaturedItemDTO
} from '../../domain/dtos/CollectionFeaturedItemsDTO'
import { ApiConstants } from '../../../core/infra/repositories/ApiConstants'
import { PublicationStatus } from '../../../core/domain/models/PublicationStatus'
import { ReadError } from '../../../core/domain/repositories/ReadError'
Expand Down Expand Up @@ -387,17 +398,31 @@ export class CollectionsRepository extends ApiRepository implements ICollections

const formData = new FormData()

orderedFeaturedItemsDTO.forEach((item) => {
const { id, content, displayOrder, file, keepFile } = item
const fileName = file ? file.name : ''

formData.append('id', id ? id.toString() : '0')
formData.append('content', content)
formData.append('displayOrder', displayOrder.toString())
formData.append('keepFile', keepFile.toString())
formData.append('fileName', fileName)
if (file) {
formData.append('file', file)
orderedFeaturedItemsDTO.forEach((item: CustomFeaturedItemDTO | DvObjectFeaturedItemDTO) => {
formData.append('id', item.id !== undefined ? item.id.toString() : '0')
formData.append('displayOrder', item.displayOrder.toString())

if (item.type === FeaturedItemType.CUSTOM) {
// CustomFeaturedItemDTO
formData.append('type', item.type)
formData.append('content', item.content)
formData.append('keepFile', item.keepFile.toString())
formData.append('fileName', item.file ? item.file.name : '')
if (item.file) {
formData.append('file', item.file)
}

// We still need to append dvObjectIdentifier as it is expected by the backend even empty
formData.append('dvObjectIdentifier', '')
} else {
// DvObjectFeaturedItemDTO
formData.append('type', domainTypeToApiType[item.type as DvObjectFeaturedItem['type']])
formData.append('dvObjectIdentifier', item.dvObjectIdentifier)

// We still need to append content, keepFile, and fileName as they are expected by the backend even empty
formData.append('content', '')
formData.append('keepFile', '')
formData.append('fileName', '')
}
})

Expand All @@ -411,4 +436,12 @@ export class CollectionsRepository extends ApiRepository implements ICollections
throw error
})
}

public async deleteCollectionFeaturedItem(featuredItemId: number): Promise<void> {
return this.doDelete(`/dataverseFeaturedItems/${featuredItemId}`)
.then(() => undefined)
.catch((error) => {
throw error
})
}
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,18 @@
export interface CollectionFeaturedItemPayload {
export type CollectionFeaturedItemPayload = CustomFeaturedItemPayload | DvObjectFeaturedItemPayload

export interface CustomFeaturedItemPayload {
id: number
type: 'custom'
content: string
imageFileName: string | null
imageFileUrl: string | null
displayOrder: number
}

export interface DvObjectFeaturedItemPayload {
id: number
type: 'dataverse' | 'dataset' | 'datafile'
dvObjectIdentifier: string
dvObjectDisplayName: string
displayOrder: number
}
Original file line number Diff line number Diff line change
@@ -1,16 +1,66 @@
import { CollectionFeaturedItem } from '../../../domain/models/CollectionFeaturedItem'
import { CollectionFeaturedItemPayload } from './CollectionFeaturedItemPayload'
import {
CollectionFeaturedItem,
CustomFeaturedItem,
DvObjectFeaturedItem,
FeaturedItemType
} from '../../../domain/models/CollectionFeaturedItem'
import {
CollectionFeaturedItemPayload,
DvObjectFeaturedItemPayload
} from './CollectionFeaturedItemPayload'

const apiTypeToDomainType: Record<
DvObjectFeaturedItemPayload['type'],
DvObjectFeaturedItem['type']
> = {
dataverse: FeaturedItemType.COLLECTION,
dataset: FeaturedItemType.DATASET,
datafile: FeaturedItemType.FILE
}

export const domainTypeToApiType: Record<
DvObjectFeaturedItem['type'],
DvObjectFeaturedItemPayload['type']
> = {
[FeaturedItemType.COLLECTION]: 'dataverse',
[FeaturedItemType.DATASET]: 'dataset',
[FeaturedItemType.FILE]: 'datafile'
}

export const transformCollectionFeaturedItemsPayloadToCollectionFeaturedItems = (
collectionFeaturedItemsPayload: CollectionFeaturedItemPayload[]
payload: CollectionFeaturedItemPayload[]
): CollectionFeaturedItem[] => {
return collectionFeaturedItemsPayload
.map((collectionFeaturedItemPayload) => ({
id: collectionFeaturedItemPayload.id,
content: collectionFeaturedItemPayload.content,
imageFileUrl: collectionFeaturedItemPayload.imageFileUrl || undefined,
imageFileName: collectionFeaturedItemPayload.imageFileName || undefined,
displayOrder: collectionFeaturedItemPayload.displayOrder
}))
return payload
.map((item) => {
if (item.type === 'custom') {
const customFeaturedItem: CustomFeaturedItem = {
id: item.id,
type: FeaturedItemType.CUSTOM,
content: item.content,
imageFileUrl: item.imageFileUrl || undefined,
imageFileName: item.imageFileName || undefined,
displayOrder: item.displayOrder
}

return customFeaturedItem
} else {
// Map API types to domain types
const type = apiTypeToDomainType[item.type]

if (!type) {
throw new Error(`Unknown type: ${item.type}`)
}

const dvObjectFeaturedItem: DvObjectFeaturedItem = {
id: item.id,
type,
dvObjectIdentifier: item.dvObjectIdentifier,
dvObjectDisplayName: item.dvObjectDisplayName,
displayOrder: item.displayOrder
}

return dvObjectFeaturedItem
}
})
.sort((a, b) => a.displayOrder - b.displayOrder)
}
4 changes: 2 additions & 2 deletions test/environment/.env
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
POSTGRES_VERSION=13
DATAVERSE_DB_USER=dataverse
SOLR_VERSION=9.8.0
DATAVERSE_IMAGE_REGISTRY=docker.io
DATAVERSE_IMAGE_TAG=unstable
DATAVERSE_IMAGE_REGISTRY=ghcr.io
DATAVERSE_IMAGE_TAG=11550-update-dv-object-featured-items-response
Comment thread
g-saracca marked this conversation as resolved.
Outdated
DATAVERSE_BOOTSTRAP_TIMEOUT=5m
70 changes: 70 additions & 0 deletions test/functional/collections/DeleteCollectionFeaturedItem.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
import { ApiConfig, deleteCollectionFeaturedItem, WriteError } from '../../../src'
import { TestConstants } from '../../testHelpers/TestConstants'
import { DataverseApiAuthMechanism } from '../../../src/core/infra/repositories/ApiConfig'
import {
createCollectionViaApi,
deleteCollectionViaApi
} from '../../testHelpers/collections/collectionHelper'
import { createCollectionCustomFeaturedItemViaApi } from '../../testHelpers/collections/collectionFeaturedItemsHelper'

describe('execute', () => {
const testCollectionAlias = 'deleteCollectionFeaturedItemTest'
let featuredItemTestId: number

beforeEach(async () => {
ApiConfig.init(
TestConstants.TEST_API_URL,
DataverseApiAuthMechanism.API_KEY,
process.env.TEST_API_KEY
)
})

beforeAll(async () => {
try {
await createCollectionViaApi(testCollectionAlias)
const featuredItem = await createCollectionCustomFeaturedItemViaApi(testCollectionAlias, {
content: '<p class="rte-paragraph">Test content</p>',
displayOrder: 1,
withFile: true,
fileName: 'featured-item-test-image.png'
})
featuredItemTestId = featuredItem.id
} catch (error) {
throw new Error(
`Tests beforeAll(): Error while creating test collection: ${testCollectionAlias}`
)
}
})

afterAll(async () => {
try {
await deleteCollectionViaApi(testCollectionAlias)
} catch (error) {
throw new Error(
`Tests afterAll(): Error while deleting test collection: ${testCollectionAlias}`
)
}
})

test('should succesfully delete the featured item', async () => {
const actual = await deleteCollectionFeaturedItem.execute(featuredItemTestId)

expect(actual).toBeUndefined()
})

test('should throw an error when featured item does not exist', async () => {
const invalidFeaturedItemId = 99
let writeError: WriteError | undefined

try {
await deleteCollectionFeaturedItem.execute(invalidFeaturedItemId)
} catch (error) {
writeError = error as WriteError
} finally {
expect(writeError).toBeInstanceOf(WriteError)
expect((writeError as WriteError).message).toEqual(
`There was an error when writing the resource. Reason was: [404] Could not find dataverse featured item with identifier ${invalidFeaturedItemId}`
)
}
})
})
Loading