Skip to content

Commit b36dcf5

Browse files
committed
Merge branch 'develop' into 237-modify-payload-create-update-collection
2 parents 10de0b2 + 3ebfc7d commit b36dcf5

31 files changed

+1315
-18
lines changed

docs/useCases.md

+78
Original file line numberDiff line numberDiff line change
@@ -14,10 +14,13 @@ The different use cases currently available in the package are classified below,
1414
- [Get Collection Facets](#get-collection-facets)
1515
- [Get User Permissions on a Collection](#get-user-permissions-on-a-collection)
1616
- [List All Collection Items](#list-all-collection-items)
17+
- [Get Collection Featured Items](#get-collection-featured-items)
1718
- [Collections write use cases](#collections-write-use-cases)
1819
- [Create a Collection](#create-a-collection)
1920
- [Update a Collection](#update-a-collection)
2021
- [Publish a Collection](#publish-a-collection)
22+
- [Update Collection Featured Items](#update-collection-featured-items)
23+
- [Delete Collection Featured Items](#delete-collection-featured-items)
2124
- [Datasets](#Datasets)
2225
- [Datasets read use cases](#datasets-read-use-cases)
2326
- [Get a Dataset](#get-a-dataset)
@@ -202,6 +205,33 @@ This use case supports the following optional parameters depending on the search
202205
- **offset**: (number) Offset for pagination.
203206
- **collectionSearchCriteria**: ([CollectionSearchCriteria](../src/collections/domain/models/CollectionSearchCriteria.ts)) Supports filtering the collection items by different properties.
204207

208+
#### Get Collection Featured Items
209+
210+
Returns a [CollectionFeaturedItem](../src/collections/domain/models/CollectionFeaturedItem.ts) array containing the featured items of the requested collection, given the collection identifier or alias.
211+
212+
##### Example call:
213+
214+
```typescript
215+
import { getCollectionFeaturedItems } from '@iqss/dataverse-client-javascript'
216+
217+
const collectionIdOrAlias = 12345
218+
219+
getCollectionFeaturedItems
220+
.execute(collectionId)
221+
.then((featuredItems: CollectionFeaturedItem[]) => {
222+
/* ... */
223+
})
224+
.catch((error: Error) => {
225+
/* ... */
226+
})
227+
```
228+
229+
_See [use case](../src/collections/domain/useCases/GetCollectionFeaturedItems.ts)_ definition.
230+
231+
The `collectionIdOrAlias` is a generic collection identifier, which can be either a string (for queries by CollectionAlias), or a number (for queries by CollectionId).
232+
233+
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.
234+
205235
### Collections Write Use Cases
206236

207237
#### Create a Collection
@@ -285,6 +315,54 @@ The `collectionIdOrAlias` is a generic collection identifier, which can be eithe
285315

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

318+
#### Update Collection Featured Items
319+
320+
Updates all featured items, given a collection identifier and a CollectionFeaturedItemsDTO.
321+
322+
##### Example call:
323+
324+
```typescript
325+
import { updateCollectionFeaturedItems } from '@iqss/dataverse-client-javascript'
326+
327+
/* ... */
328+
329+
const collectionIdOrAlias = 12345
330+
331+
updateCollectionFeaturedItems
332+
.execute(collectionIdOrAlias)
333+
.then((collectionFeaturedItems: CollectionFeaturedItem[]) => {
334+
/* ... */
335+
})
336+
337+
/* ... */
338+
```
339+
340+
The `collectionIdOrAlias` is a generic collection identifier, which can be either a string (for queries by CollectionAlias), or a number (for queries by CollectionId).
341+
342+
_See [use case](../src/collections/domain/useCases/UpdateCollectionFeaturedItems.ts)_ definition.
343+
344+
#### Delete Collection Featured Items
345+
346+
Deletes all featured items from a collection, given a collection identifier.
347+
348+
##### Example call:
349+
350+
```typescript
351+
import { deleteCollectionFeaturedItems } from '@iqss/dataverse-client-javascript'
352+
353+
/* ... */
354+
355+
const collectionIdOrAlias = 12345
356+
357+
deleteCollectionFeaturedItems.execute(collectionIdOrAlias)
358+
359+
/* ... */
360+
```
361+
362+
The `collectionIdOrAlias` is a generic collection identifier, which can be either a string (for queries by CollectionAlias), or a number (for queries by CollectionId).
363+
364+
_See [use case](../src/collections/domain/useCases/DeleteCollectionFeaturedItems.ts)_ definition.
365+
288366
## Datasets
289367

290368
### Datasets Read Use Cases
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
export type CollectionFeaturedItemsDTO = CollectionFeaturedItemDTO[]
2+
3+
export interface CollectionFeaturedItemDTO {
4+
id?: number
5+
content: string
6+
displayOrder: number
7+
file?: File
8+
keepFile: boolean
9+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
export interface CollectionFeaturedItem {
2+
id: number
3+
content: string
4+
imageFileName?: string
5+
imageFileUrl?: string
6+
displayOrder: number
7+
}

src/collections/domain/repositories/ICollectionsRepository.ts

+10
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
import { CollectionDTO } from '../dtos/CollectionDTO'
2+
import { CollectionFeaturedItemsDTO } from '../dtos/CollectionFeaturedItemsDTO'
23
import { Collection } from '../models/Collection'
34
import { CollectionFacet } from '../models/CollectionFacet'
5+
import { CollectionFeaturedItem } from '../models/CollectionFeaturedItem'
46
import { CollectionItemSubset } from '../models/CollectionItemSubset'
57
import { CollectionSearchCriteria } from '../models/CollectionSearchCriteria'
68
import { CollectionUserPermissions } from '../models/CollectionUserPermissions'
@@ -26,4 +28,12 @@ export interface ICollectionsRepository {
2628
collectionIdOrAlias: number | string,
2729
updatedCollection: CollectionDTO
2830
): Promise<void>
31+
getCollectionFeaturedItems(
32+
collectionIdOrAlias: number | string
33+
): Promise<CollectionFeaturedItem[]>
34+
updateCollectionFeaturedItems(
35+
collectionIdOrAlias: number | string,
36+
featuredItemDTOs: CollectionFeaturedItemsDTO
37+
): Promise<CollectionFeaturedItem[]>
38+
deleteCollectionFeaturedItems(collectionIdOrAlias: number | string): Promise<void>
2939
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
import { UseCase } from '../../../core/domain/useCases/UseCase'
2+
import { ROOT_COLLECTION_ID } from '../models/Collection'
3+
import { ICollectionsRepository } from '../repositories/ICollectionsRepository'
4+
5+
export class DeleteCollectionFeaturedItems implements UseCase<void> {
6+
private collectionsRepository: ICollectionsRepository
7+
8+
constructor(collectionsRepository: ICollectionsRepository) {
9+
this.collectionsRepository = collectionsRepository
10+
}
11+
12+
/**
13+
* Deletes all featured items from a collection, given a collection identifier.
14+
*
15+
* @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)
16+
* If this parameter is not set, the default value is: ':root'
17+
* @returns {Promise<void>} - This method does not return anything upon successful completion.
18+
* @throws {WriteError} - If there are errors while writing data.
19+
*/
20+
async execute(collectionIdOrAlias: number | string = ROOT_COLLECTION_ID): Promise<void> {
21+
return await this.collectionsRepository.deleteCollectionFeaturedItems(collectionIdOrAlias)
22+
}
23+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
import { UseCase } from '../../../core/domain/useCases/UseCase'
2+
import { ICollectionsRepository } from '../repositories/ICollectionsRepository'
3+
import { ROOT_COLLECTION_ID } from '../models/Collection'
4+
import { CollectionFeaturedItem } from '../models/CollectionFeaturedItem'
5+
6+
export class GetCollectionFeaturedItems implements UseCase<CollectionFeaturedItem[]> {
7+
private collectionsRepository: ICollectionsRepository
8+
9+
constructor(collectionsRepository: ICollectionsRepository) {
10+
this.collectionsRepository = collectionsRepository
11+
}
12+
13+
/**
14+
* Returns a CollectionFeaturedItem array containing the featured items of the requested collection, given the collection identifier or alias.
15+
*
16+
* @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)
17+
* If this parameter is not set, the default value is: ':root'
18+
* @returns {Promise<CollectionFeaturedItem[]>}
19+
*/
20+
async execute(
21+
collectionIdOrAlias: number | string = ROOT_COLLECTION_ID
22+
): Promise<CollectionFeaturedItem[]> {
23+
return await this.collectionsRepository.getCollectionFeaturedItems(collectionIdOrAlias)
24+
}
25+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
import { UseCase } from '../../../core/domain/useCases/UseCase'
2+
import { CollectionFeaturedItemsDTO } from '../dtos/CollectionFeaturedItemsDTO'
3+
import { ROOT_COLLECTION_ID } from '../models/Collection'
4+
import { CollectionFeaturedItem } from '../models/CollectionFeaturedItem'
5+
import { ICollectionsRepository } from '../repositories/ICollectionsRepository'
6+
7+
export class UpdateCollectionFeaturedItems implements UseCase<CollectionFeaturedItem[]> {
8+
private collectionsRepository: ICollectionsRepository
9+
10+
constructor(collectionsRepository: ICollectionsRepository) {
11+
this.collectionsRepository = collectionsRepository
12+
}
13+
14+
/**
15+
* Updates all featured items, given a collection identifier and a CollectionFeaturedItemsDTO.
16+
*
17+
* @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)
18+
* If this parameter is not set, the default value is: ':root'
19+
* @param {CollectionFeaturedItemsDTO} [newCollectionFeaturedItems] - CollectionFeaturedItemsDTO object including the updated collection featured items data.
20+
* @returns {Promise<CollectionFeaturedItem[]>} -This method returns the updated collection featured items upon successful completion.
21+
* @throws {WriteError} - If there are errors while writing data.
22+
*/
23+
async execute(
24+
collectionIdOrAlias: number | string = ROOT_COLLECTION_ID,
25+
featuredItemsDTO: CollectionFeaturedItemsDTO
26+
): Promise<CollectionFeaturedItem[]> {
27+
return await this.collectionsRepository.updateCollectionFeaturedItems(
28+
collectionIdOrAlias,
29+
featuredItemsDTO
30+
)
31+
}
32+
}

src/collections/index.ts

+12-2
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,10 @@ import { GetCollectionUserPermissions } from './domain/useCases/GetCollectionUse
55
import { GetCollectionItems } from './domain/useCases/GetCollectionItems'
66
import { PublishCollection } from './domain/useCases/PublishCollection'
77
import { UpdateCollection } from './domain/useCases/UpdateCollection'
8-
8+
import { GetCollectionFeaturedItems } from './domain/useCases/GetCollectionFeaturedItems'
99
import { CollectionsRepository } from './infra/repositories/CollectionsRepository'
10+
import { UpdateCollectionFeaturedItems } from './domain/useCases/UpdateCollectionFeaturedItems'
11+
import { DeleteCollectionFeaturedItems } from './domain/useCases/DeleteCollectionFeaturedItems'
1012

1113
const collectionsRepository = new CollectionsRepository()
1214

@@ -17,6 +19,9 @@ const getCollectionUserPermissions = new GetCollectionUserPermissions(collection
1719
const getCollectionItems = new GetCollectionItems(collectionsRepository)
1820
const publishCollection = new PublishCollection(collectionsRepository)
1921
const updateCollection = new UpdateCollection(collectionsRepository)
22+
const getCollectionFeaturedItems = new GetCollectionFeaturedItems(collectionsRepository)
23+
const updateCollectionFeaturedItems = new UpdateCollectionFeaturedItems(collectionsRepository)
24+
const deleteCollectionFeaturedItems = new DeleteCollectionFeaturedItems(collectionsRepository)
2025

2126
export {
2227
getCollection,
@@ -25,7 +30,10 @@ export {
2530
getCollectionUserPermissions,
2631
getCollectionItems,
2732
publishCollection,
28-
updateCollection
33+
updateCollection,
34+
getCollectionFeaturedItems,
35+
updateCollectionFeaturedItems,
36+
deleteCollectionFeaturedItems
2937
}
3038
export { Collection, CollectionInputLevel } from './domain/models/Collection'
3139
export { CollectionFacet } from './domain/models/CollectionFacet'
@@ -34,3 +42,5 @@ export { CollectionDTO, CollectionInputLevelDTO } from './domain/dtos/Collection
3442
export { CollectionPreview } from './domain/models/CollectionPreview'
3543
export { CollectionItemType } from './domain/models/CollectionItemType'
3644
export { CollectionSearchCriteria } from './domain/models/CollectionSearchCriteria'
45+
export { CollectionFeaturedItem } from './domain/models/CollectionFeaturedItem'
46+
export { CollectionFeaturedItemsDTO } from './domain/dtos/CollectionFeaturedItemsDTO'

src/collections/infra/repositories/CollectionsRepository.ts

+67
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,10 @@ import {
1717
SortType
1818
} from '../../domain/models/CollectionSearchCriteria'
1919
import { CollectionItemType } from '../../domain/models/CollectionItemType'
20+
import { CollectionFeaturedItem } from '../../domain/models/CollectionFeaturedItem'
21+
import { transformCollectionFeaturedItemsPayloadToCollectionFeaturedItems } from './transformers/collectionFeaturedItemsTransformer'
22+
import { CollectionFeaturedItemsDTO } from '../../domain/dtos/CollectionFeaturedItemsDTO'
23+
import { ApiConstants } from '../../../core/infra/repositories/ApiConstants'
2024

2125
export interface NewCollectionRequestPayload {
2226
alias: string
@@ -272,4 +276,67 @@ export class CollectionsRepository extends ApiRepository implements ICollections
272276
})
273277
}
274278
}
279+
280+
public async getCollectionFeaturedItems(
281+
collectionIdOrAlias: number | string
282+
): Promise<CollectionFeaturedItem[]> {
283+
return this.doGet(`/${this.collectionsResourceName}/${collectionIdOrAlias}/featuredItems`, true)
284+
.then((response) =>
285+
transformCollectionFeaturedItemsPayloadToCollectionFeaturedItems(response.data.data)
286+
)
287+
.catch((error) => {
288+
throw error
289+
})
290+
}
291+
292+
public async updateCollectionFeaturedItems(
293+
collectionIdOrAlias: number | string,
294+
featuredItemsDTO: CollectionFeaturedItemsDTO
295+
): Promise<CollectionFeaturedItem[]> {
296+
const featuredItemsFormData = this.toFeaturedItemsFormData(featuredItemsDTO)
297+
298+
return this.doPut(
299+
`/${this.collectionsResourceName}/${collectionIdOrAlias}/featuredItems`,
300+
featuredItemsFormData,
301+
undefined,
302+
ApiConstants.CONTENT_TYPE_MULTIPART_FORM_DATA
303+
)
304+
.then((response) =>
305+
transformCollectionFeaturedItemsPayloadToCollectionFeaturedItems(response.data.data)
306+
)
307+
.catch((error) => {
308+
throw error
309+
})
310+
}
311+
312+
private toFeaturedItemsFormData(featuredItemsDTO: CollectionFeaturedItemsDTO): FormData {
313+
// 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
314+
const orderedFeaturedItemsDTO = featuredItemsDTO.sort((a, b) => a.displayOrder - b.displayOrder)
315+
316+
const formData = new FormData()
317+
318+
orderedFeaturedItemsDTO.forEach((item) => {
319+
const { id, content, displayOrder, file, keepFile } = item
320+
const fileName = file ? file.name : ''
321+
322+
formData.append('id', id ? id.toString() : '0')
323+
formData.append('content', content)
324+
formData.append('displayOrder', displayOrder.toString())
325+
formData.append('keepFile', keepFile.toString())
326+
formData.append('fileName', fileName)
327+
if (file) {
328+
formData.append('file', file)
329+
}
330+
})
331+
332+
return formData
333+
}
334+
335+
public async deleteCollectionFeaturedItems(collectionIdOrAlias: number | string): Promise<void> {
336+
return this.doDelete(`/${this.collectionsResourceName}/${collectionIdOrAlias}/featuredItems`)
337+
.then(() => undefined)
338+
.catch((error) => {
339+
throw error
340+
})
341+
}
275342
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
export interface CollectionFeaturedItemPayload {
2+
id: number
3+
content: string
4+
imageFileName: string | null
5+
imageFileUrl: string | null
6+
displayOrder: number
7+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
import { CollectionFeaturedItem } from '../../../domain/models/CollectionFeaturedItem'
2+
import { CollectionFeaturedItemPayload } from './CollectionFeaturedItemPayload'
3+
4+
export const transformCollectionFeaturedItemsPayloadToCollectionFeaturedItems = (
5+
collectionFeaturedItemsPayload: CollectionFeaturedItemPayload[]
6+
): CollectionFeaturedItem[] => {
7+
return collectionFeaturedItemsPayload
8+
.map((collectionFeaturedItemPayload) => ({
9+
id: collectionFeaturedItemPayload.id,
10+
content: collectionFeaturedItemPayload.content,
11+
imageFileUrl: collectionFeaturedItemPayload.imageFileUrl || undefined,
12+
imageFileName: collectionFeaturedItemPayload.imageFileName || undefined,
13+
displayOrder: collectionFeaturedItemPayload.displayOrder
14+
}))
15+
.sort((a, b) => a.displayOrder - b.displayOrder)
16+
}

src/core/infra/repositories/ApiRepository.ts

+3-2
Original file line numberDiff line numberDiff line change
@@ -30,9 +30,10 @@ export abstract class ApiRepository {
3030
public async doPut(
3131
apiEndpoint: string,
3232
data: string | object,
33-
queryParams: object = {}
33+
queryParams: object = {},
34+
contentType: string = ApiConstants.CONTENT_TYPE_APPLICATION_JSON
3435
): Promise<AxiosResponse> {
35-
return await this.doRequest('put', apiEndpoint, data, queryParams)
36+
return await this.doRequest('put', apiEndpoint, data, queryParams, contentType)
3637
}
3738

3839
public async doDelete(apiEndpoint: string, queryParams: object = {}): Promise<AxiosResponse> {

src/core/infra/repositories/apiConfigBuilders.ts

+5
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,11 @@ export const buildRequestConfig = (
1616
...(abortSignal && { signal: abortSignal })
1717
}
1818

19+
// When using multipart/form-data for axios to work properly its better to avoid setting the content-type and let the browser manage it
20+
if (contentType === ApiConstants.CONTENT_TYPE_MULTIPART_FORM_DATA) {
21+
requestConfig.headers['Content-Type'] = undefined
22+
}
23+
1924
if (!authRequired) {
2025
return requestConfig
2126
}

0 commit comments

Comments
 (0)