Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Featured Items Use Cases #235

Draft
wants to merge 6 commits into
base: develop
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all 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
55 changes: 55 additions & 0 deletions docs/useCases.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,12 @@ 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)
- [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)
Expand Down Expand Up @@ -202,6 +204,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
Expand Down Expand Up @@ -285,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
Expand Down
9 changes: 9 additions & 0 deletions src/collections/domain/dtos/CollectionFeaturedItemsDTO.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
export type CollectionFeaturedItemsDTO = CollectionFeaturedItemDTO[]

export interface CollectionFeaturedItemDTO {
id?: number
content: string
displayOrder: number
file?: File
keepFile: boolean
}
7 changes: 7 additions & 0 deletions src/collections/domain/models/CollectionFeaturedItem.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
export interface CollectionFeaturedItem {
id: number
content: string
imageFileName?: string
imageFileUrl?: string
displayOrder: number
}
9 changes: 9 additions & 0 deletions src/collections/domain/repositories/ICollectionsRepository.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
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'
import { CollectionItemSubset } from '../models/CollectionItemSubset'
import { CollectionSearchCriteria } from '../models/CollectionSearchCriteria'
import { CollectionUserPermissions } from '../models/CollectionUserPermissions'
Expand All @@ -26,4 +28,11 @@ export interface ICollectionsRepository {
collectionIdOrAlias: number | string,
updatedCollection: CollectionDTO
): Promise<void>
getCollectionFeaturedItems(
collectionIdOrAlias: number | string
): Promise<CollectionFeaturedItem[]>
updateCollectionFeaturedItems(
collectionIdOrAlias: number | string,
featuredItemDTOs: CollectionFeaturedItemsDTO
): Promise<CollectionFeaturedItem[]>
}
25 changes: 25 additions & 0 deletions src/collections/domain/useCases/GetCollectionFeaturedItems.ts
Original file line number Diff line number Diff line change
@@ -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<CollectionFeaturedItem[]> {
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<CollectionFeaturedItem[]>}
*/
async execute(
collectionIdOrAlias: number | string = ROOT_COLLECTION_ID
): Promise<CollectionFeaturedItem[]> {
return await this.collectionsRepository.getCollectionFeaturedItems(collectionIdOrAlias)
}
}
32 changes: 32 additions & 0 deletions src/collections/domain/useCases/UpdateCollectionFeaturedItems.ts
Original file line number Diff line number Diff line change
@@ -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<CollectionFeaturedItem[]> {
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<CollectionFeaturedItem[]>} -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<CollectionFeaturedItem[]> {
return await this.collectionsRepository.updateCollectionFeaturedItems(
collectionIdOrAlias,
featuredItemsDTO
)
}
}
11 changes: 9 additions & 2 deletions src/collections/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,9 @@ 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'
import { UpdateCollectionFeaturedItems } from './domain/useCases/UpdateCollectionFeaturedItems'

const collectionsRepository = new CollectionsRepository()

Expand All @@ -17,6 +18,8 @@ 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)
const updateCollectionFeaturedItems = new UpdateCollectionFeaturedItems(collectionsRepository)

export {
getCollection,
Expand All @@ -25,7 +28,9 @@ export {
getCollectionUserPermissions,
getCollectionItems,
publishCollection,
updateCollection
updateCollection,
getCollectionFeaturedItems,
updateCollectionFeaturedItems
}
export { Collection, CollectionInputLevel } from './domain/models/Collection'
export { CollectionFacet } from './domain/models/CollectionFacet'
Expand All @@ -34,3 +39,5 @@ export { CollectionDTO, CollectionInputLevelDTO } from './domain/dtos/Collection
export { CollectionPreview } from './domain/models/CollectionPreview'
export { CollectionItemType } from './domain/models/CollectionItemType'
export { CollectionSearchCriteria } from './domain/models/CollectionSearchCriteria'
export { CollectionFeaturedItem } from './domain/models/CollectionFeaturedItem'
export { CollectionFeaturedItemsDTO } from './domain/dtos/CollectionFeaturedItemsDTO'
59 changes: 59 additions & 0 deletions src/collections/infra/repositories/CollectionsRepository.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,10 @@ 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 { ApiConstants } from '../../../core/infra/repositories/ApiConstants'

export interface NewCollectionRequestPayload {
alias: string
Expand Down Expand Up @@ -240,4 +244,59 @@ export class CollectionsRepository extends ApiRepository implements ICollections
})
}
}

public async getCollectionFeaturedItems(
collectionIdOrAlias: number | string
): Promise<CollectionFeaturedItem[]> {
return this.doGet(`/${this.collectionsResourceName}/${collectionIdOrAlias}/featuredItems`, true)
.then((response) =>
transformCollectionFeaturedItemsPayloadToCollectionFeaturedItems(response.data.data)
)
.catch((error) => {
throw error
})
}

public async updateCollectionFeaturedItems(
collectionIdOrAlias: number | string,
featuredItemsDTO: CollectionFeaturedItemsDTO
): Promise<CollectionFeaturedItem[]> {
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
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
export interface CollectionFeaturedItemPayload {
id: number
content: string
imageFileName: string | null
imageFileUrl: string | null
displayOrder: number
}
Original file line number Diff line number Diff line change
@@ -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
}))
}
5 changes: 3 additions & 2 deletions src/core/infra/repositories/ApiRepository.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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<AxiosResponse> {
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<AxiosResponse> {
Expand Down
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.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
Loading
Loading