Skip to content

Commit 79b5a8d

Browse files
committed
feat(collections): support partial updates in updateCollection via Partial<CollectionDTO> and dedicated request builder @createUpdateRequestBody
1 parent d2c729c commit 79b5a8d

5 files changed

Lines changed: 138 additions & 7 deletions

File tree

CHANGELOG.md

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -23,15 +23,13 @@ This changelog follows the principles of [Keep a Changelog](https://keepachangel
2323
- Templates: Rename `CreateDatasetTemplateDTO` to `CreateTemplateDTO`.
2424
- Templates: Rename `createDatasetTemplate` repository method to `createTemplate`.
2525
- Templates: Rename `getDatasetTemplates` repository method to `getTemplatesByCollectionId`.
26+
- Collections: `updateCollection` now supports partial updates by accepting `Partial<CollectionDTO>`. Only explicitly provided fields are sent in update requests, aligning with Dataverse API semantics. Metadata blocks handling was adjusted to respect inheritance flags and avoid invalid field combinations.
2627

2728
### Fixed
2829

29-
- Allow partial CollectionDTO for UpdateCollection use case
30-
3130
- In GetAllNotificationsByUser use case, additionalInfo field is returned as an object instead of a string.
3231
- In GetAllNotificationsByUser use case, added support for filtering unread messages and pagination.
3332

34-
3533
### Removed
3634

3735
- Removed date fields validations in create and update dataset use cases, since validation is already handled in the backend and SPA frontend (other clients should perform client side validation also). This avoids duplicated logic and keeps the package focused on its core responsibility.

package-lock.json

Lines changed: 10 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/collections/infra/repositories/CollectionsRepository.ts

Lines changed: 82 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -215,9 +215,9 @@ export class CollectionsRepository extends ApiRepository implements ICollections
215215

216216
public async updateCollection(
217217
collectionIdOrAlias: string | number,
218-
updatedCollection: CollectionDTO
218+
updatedCollection: Partial<CollectionDTO>
219219
): Promise<void> {
220-
const requestBody = this.createCreateOrUpdateRequestBody(updatedCollection)
220+
const requestBody = this.createUpdateRequestBody(updatedCollection)
221221

222222
return this.doPut(`/${this.collectionsResourceName}/${collectionIdOrAlias}`, requestBody)
223223
.then(() => undefined)
@@ -332,6 +332,86 @@ export class CollectionsRepository extends ApiRepository implements ICollections
332332
}
333333
}
334334

335+
private createUpdateRequestBody(
336+
collectionDTO: Partial<CollectionDTO>
337+
): Partial<NewCollectionRequestPayload> {
338+
const dataverseContacts: NewCollectionContactRequestPayload[] | undefined =
339+
collectionDTO.contacts?.map((contact) => ({
340+
contactEmail: contact
341+
}))
342+
const inputLevelsRequestBody: NewCollectionInputLevelRequestPayload[] | undefined =
343+
collectionDTO.inputLevels?.map((inputLevel) => ({
344+
datasetFieldTypeName: inputLevel.datasetFieldName,
345+
include: inputLevel.include,
346+
required: inputLevel.required
347+
}))
348+
let metadataBlocksRequestBody: Partial<NewCollectionMetadataBlocksRequestPayload> | undefined
349+
350+
const hasMetadataBlocksData =
351+
collectionDTO.metadataBlockNames !== undefined ||
352+
collectionDTO.facetIds !== undefined ||
353+
collectionDTO.inputLevels !== undefined ||
354+
collectionDTO.inheritMetadataBlocksFromParent !== undefined ||
355+
collectionDTO.inheritFacetsFromParent !== undefined
356+
357+
if (hasMetadataBlocksData) {
358+
metadataBlocksRequestBody = {}
359+
if (collectionDTO.inheritMetadataBlocksFromParent !== true) {
360+
if (collectionDTO.metadataBlockNames !== undefined) {
361+
metadataBlocksRequestBody.metadataBlockNames = collectionDTO.metadataBlockNames
362+
}
363+
if (inputLevelsRequestBody !== undefined) {
364+
metadataBlocksRequestBody.inputLevels = inputLevelsRequestBody
365+
}
366+
}
367+
if (collectionDTO.inheritFacetsFromParent !== true) {
368+
if (collectionDTO.facetIds !== undefined) {
369+
metadataBlocksRequestBody.facetIds = collectionDTO.facetIds
370+
}
371+
}
372+
if (collectionDTO.inheritMetadataBlocksFromParent !== undefined) {
373+
metadataBlocksRequestBody.inheritMetadataBlocksFromParent =
374+
collectionDTO.inheritMetadataBlocksFromParent
375+
}
376+
if (collectionDTO.inheritFacetsFromParent !== undefined) {
377+
metadataBlocksRequestBody.inheritFacetsFromParent = collectionDTO.inheritFacetsFromParent
378+
}
379+
}
380+
381+
// Build the final request body, only including defined fields
382+
const requestBody: Partial<NewCollectionRequestPayload> = {}
383+
384+
if (collectionDTO.alias !== undefined) {
385+
requestBody.alias = collectionDTO.alias
386+
}
387+
388+
if (collectionDTO.name !== undefined) {
389+
requestBody.name = collectionDTO.name
390+
}
391+
392+
if (dataverseContacts !== undefined) {
393+
requestBody.dataverseContacts = dataverseContacts
394+
}
395+
396+
if (collectionDTO.type !== undefined) {
397+
requestBody.dataverseType = collectionDTO.type
398+
}
399+
400+
if (collectionDTO.description !== undefined) {
401+
requestBody.description = collectionDTO.description
402+
}
403+
404+
if (collectionDTO.affiliation !== undefined) {
405+
requestBody.affiliation = collectionDTO.affiliation
406+
}
407+
408+
if (metadataBlocksRequestBody !== undefined) {
409+
requestBody.metadataBlocks = metadataBlocksRequestBody
410+
}
411+
412+
return requestBody
413+
}
414+
335415
private applyCollectionSearchCriteriaToQueryParams(
336416
queryParams: URLSearchParams,
337417
collectionSearchCriteria: CollectionSearchCriteria

test/functional/collections/UpdateCollection.test.ts

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,8 @@ import {
33
WriteError,
44
createCollection,
55
getCollection,
6-
updateCollection
6+
updateCollection,
7+
CollectionDTO
78
} from '../../../src'
89
import { TestConstants } from '../../testHelpers/TestConstants'
910
import { DataverseApiAuthMechanism } from '../../../src/core/infra/repositories/ApiConfig'
@@ -35,6 +36,28 @@ describe('execute', () => {
3536
}
3637
})
3738

39+
test('should successfully update a collection with partial data (name only)', async () => {
40+
const testNewCollectionAlias = 'updateCollection-partial-test'
41+
const testNewCollection = createCollectionDTO(testNewCollectionAlias)
42+
await createCollection.execute(testNewCollection)
43+
44+
const partialUpdate: Partial<CollectionDTO> = {
45+
name: 'Partially Updated Name'
46+
}
47+
48+
expect.assertions(3)
49+
try {
50+
await updateCollection.execute(testNewCollectionAlias, partialUpdate)
51+
} catch (error) {
52+
throw new Error('Collection should be updated with partial data')
53+
} finally {
54+
const updatedCollection = await getCollection.execute(testNewCollectionAlias)
55+
expect(updatedCollection.name).toBe('Partially Updated Name')
56+
expect(updatedCollection.alias).toBe(testNewCollectionAlias)
57+
expect(updatedCollection.type).toBe(testNewCollection.type)
58+
}
59+
})
60+
3861
test('should throw an error when the parent collection does not exist', async () => {
3962
const testNewCollection = createCollectionDTO()
4063
expect.assertions(2)

test/integration/collections/CollectionsRepository.test.ts

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1110,6 +1110,27 @@ describe('CollectionsRepository', () => {
11101110
expect(updatedInputLevel?.required).toBe(false)
11111111
})
11121112

1113+
test('should update collection with only partial fields (name and affiliation)', async () => {
1114+
const collectionDTO = createCollectionDTO('partial-update-test')
1115+
const testCollectionId = await sut.createCollection(collectionDTO)
1116+
const createdCollection = await sut.getCollection(testCollectionId)
1117+
const partialUpdate: Partial<CollectionDTO> = {
1118+
name: 'Partially Updated Name',
1119+
affiliation: 'New Affiliation'
1120+
}
1121+
1122+
await sut.updateCollection(testCollectionId, partialUpdate)
1123+
const updatedCollection = await sut.getCollection(testCollectionId)
1124+
1125+
expect(updatedCollection.name).toBe('Partially Updated Name')
1126+
expect(updatedCollection.affiliation).toBe('New Affiliation')
1127+
expect(updatedCollection.alias).toBe(createdCollection.alias)
1128+
expect(updatedCollection.type).toBe(createdCollection.type)
1129+
expect(updatedCollection.contacts).toEqual(createdCollection.contacts)
1130+
1131+
await deleteCollectionViaApi(collectionDTO.alias)
1132+
})
1133+
11131134
test('should update the collection to inherit metadata blocks from parent collection', async () => {
11141135
const parentCollectionAlias = 'inherit-metablocks-parent-update'
11151136
const parentCollectionDTO = createCollectionDTO(parentCollectionAlias)

0 commit comments

Comments
 (0)