Skip to content

Commit 09a4048

Browse files
committed
feat: add sourceLastUpdateTime argument to update dataset metadata use case
1 parent 9366f9b commit 09a4048

File tree

8 files changed

+78
-8
lines changed

8 files changed

+78
-8
lines changed

src/files/domain/models/FileModel.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ export interface FileModel {
3030
tabularTags?: string[]
3131
creationDate?: string
3232
publicationDate?: string
33+
lastUpdateTime: string
3334
deleted: boolean
3435
tabularData: boolean
3536
fileAccessRequest?: boolean

src/files/domain/repositories/IFilesRepository.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,8 @@ export interface IFilesRepository {
7272

7373
updateFileMetadata(
7474
fileId: number | string,
75-
updateFileMetadataDTO: UpdateFileMetadataDTO
75+
updateFileMetadataDTO: UpdateFileMetadataDTO,
76+
sourceLastUpdateTime?: string
7677
): Promise<void>
7778

7879
updateFileTabularTags(

src/files/domain/useCases/UpdateFileMetadata.ts

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,12 +15,18 @@ export class UpdateFileMetadata implements UseCase<void> {
1515
*
1616
* @param {number | string} [fileId] - The file identifier, which can be a string (for persistent identifiers), or a number (for numeric identifiers).
1717
* @param {UpdateFileMetadataDTO} [updateFileMetadataDTO] - The DTO containing the metadata updates.
18+
* @param {string} [sourceLastUpdateTime] - The lastUpdateTime value from the file. If another user updates the file metadata before you send the update request, data inconsistencies may occur. To prevent this, you can use the optional sourceLastUpdateTime parameter. This parameter must include the lastUpdateTime value corresponding to the file being updated.
1819
* @returns {Promise<void>}
1920
*/
2021
async execute(
2122
fileId: number | string,
22-
updateFileMetadataDTO: UpdateFileMetadataDTO
23+
updateFileMetadataDTO: UpdateFileMetadataDTO,
24+
sourceLastUpdateTime?: string
2325
): Promise<void> {
24-
await this.filesRepository.updateFileMetadata(fileId, updateFileMetadataDTO)
26+
await this.filesRepository.updateFileMetadata(
27+
fileId,
28+
updateFileMetadataDTO,
29+
sourceLastUpdateTime
30+
)
2531
}
2632
}

src/files/infra/repositories/FilesRepository.ts

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -369,15 +369,18 @@ export class FilesRepository extends ApiRepository implements IFilesRepository {
369369

370370
public async updateFileMetadata(
371371
fileId: string | number,
372-
updateFileMetadata: UpdateFileMetadataDTO
372+
updateFileMetadata: UpdateFileMetadataDTO,
373+
sourceLastUpdateTime?: string
373374
): Promise<void> {
374375
const formData = new FormData()
375376
formData.append('jsonData', JSON.stringify(updateFileMetadata))
376377

377378
return this.doPost(
378379
this.buildApiEndpoint(this.filesResourceName, `${fileId}/metadata`),
379380
formData,
380-
{},
381+
{
382+
...(sourceLastUpdateTime && { sourceLastUpdateTime })
383+
},
381384
ApiConstants.CONTENT_TYPE_MULTIPART_FORM_DATA
382385
)
383386
.then(() => undefined)

src/files/infra/repositories/transformers/FilePayload.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ export interface FilePayload {
2929
tabularTags?: string[]
3030
creationDate?: string
3131
publicationDate?: string
32+
lastUpdateTime: string
3233
deleted: boolean
3334
tabularData: boolean
3435
fileAccessRequest?: boolean

src/files/infra/repositories/transformers/fileTransformers.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -93,7 +93,8 @@ const transformFilePayloadToFile = (filePayload: FilePayload): FileModel => {
9393
}),
9494
...(filePayload.dataFile.isPartOf && {
9595
isPartOf: transformPayloadToOwnerNode(filePayload.dataFile.isPartOf)
96-
})
96+
}),
97+
lastUpdateTime: filePayload.dataFile.lastUpdateTime
9798
}
9899
}
99100

test/integration/files/FilesRepository.test.ts

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -767,6 +767,61 @@ describe('FilesRepository', () => {
767767
errorExpected
768768
)
769769
})
770+
771+
test('should throw error when using outdated sourceLastUpdateTime', async () => {
772+
const newDatasetIds = await createDataset.execute(TestConstants.TEST_NEW_DATASET_DTO)
773+
await uploadFileViaApi(newDatasetIds.numericId, testTextFile1Name)
774+
const filesSubset = await sut.getDatasetFiles(
775+
newDatasetIds.numericId,
776+
latestDatasetVersionId,
777+
false,
778+
FileOrderCriteria.NAME_AZ
779+
)
780+
const fileId = filesSubset.files[0].id
781+
782+
await registerFileViaApi(fileId)
783+
784+
// Fetch file to obtain initial lastUpdateTime from returned model including dataset version
785+
const fileInfo: FileModel = (await sut.getFile(
786+
fileId,
787+
DatasetNotNumberedVersion.LATEST,
788+
false,
789+
false
790+
)) as FileModel
791+
792+
const lastUpdateTimeOne = fileInfo.lastUpdateTime
793+
794+
// Wait for 2 seconds
795+
await new Promise((resolve) => setTimeout(resolve, 2_000))
796+
797+
// First update using correct lastUpdateTime should succeed
798+
await sut.updateFileMetadata(fileId, { description: 'First update desc.' }, lastUpdateTimeOne)
799+
800+
// Refetch to get new lastUpdateTime
801+
const fileInfoAfterFirstUpdate: FileModel = (await sut.getFile(
802+
fileId,
803+
DatasetNotNumberedVersion.LATEST,
804+
false,
805+
false
806+
)) as FileModel
807+
808+
const lastUpdateTimeTwo = fileInfoAfterFirstUpdate.lastUpdateTime
809+
810+
expect(lastUpdateTimeTwo).not.toBe(lastUpdateTimeOne)
811+
812+
// Wait for 2 seconds
813+
await new Promise((resolve) => setTimeout(resolve, 2_000))
814+
815+
// Second update using stale lastUpdateTimeOne should fail
816+
const expectedError = new WriteError(
817+
`[400] Internal version timestamp ${lastUpdateTimeOne} is outdated`
818+
)
819+
await expect(
820+
sut.updateFileMetadata(fileId, { description: 'Second update attempt.' }, lastUpdateTimeOne)
821+
).rejects.toThrow(expectedError)
822+
823+
await deletePublishedDatasetViaApi(newDatasetIds.persistentId)
824+
})
770825
})
771826

772827
describe('updateFileTabularTags', () => {

test/testHelpers/files/filesHelper.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,8 @@ export const createFileModel = (): FileModel => {
6060
originalSize: 127426,
6161
originalName: 'originalName',
6262
tabularTags: ['tag1', 'tag2'],
63-
publicationDate: '2023-07-11'
63+
publicationDate: '2023-07-11',
64+
lastUpdateTime: '2023-07-11'
6465
}
6566
}
6667

@@ -122,7 +123,8 @@ export const createFilePayload = (): FilePayload => {
122123
originalSize: 127426,
123124
originalName: 'originalName',
124125
tabularTags: ['tag1', 'tag2'],
125-
publicationDate: '2023-07-11'
126+
publicationDate: '2023-07-11',
127+
lastUpdateTime: '2023-07-11'
126128
}
127129
}
128130
}

0 commit comments

Comments
 (0)