Skip to content
Merged
Show file tree
Hide file tree
Changes from 7 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
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ This changelog follows the principles of [Keep a Changelog](https://keepachangel
### Changed

- Standardize Node.js version to 22 across all environments (docker dev environment, CI, production).
- Use of the new `sourceLastUpdateTime` query parameter from update dataset and file metadata endpoints to support optimistic concurrency control during editing operations. See [Edit Dataset Metadata](https://guides.dataverse.org/en/6.8/api/native-api.html#edit-dataset-metadata) and [Updating File Metadata](https://guides.dataverse.org/en/6.8/api/native-api.html#updating-file-metadata) guides for more details.

### Fixed

Expand Down
8 changes: 4 additions & 4 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
"@dnd-kit/sortable": "8.0.0",
"@dnd-kit/utilities": "3.2.2",
"@faker-js/faker": "7.6.0",
"@iqss/dataverse-client-javascript": "2.0.0-alpha.62",
"@iqss/dataverse-client-javascript": "2.0.0-pr379.a1cef4b",
"@iqss/dataverse-design-system": "*",
"@istanbuljs/nyc-config-typescript": "1.0.2",
"@tanstack/react-table": "8.9.2",
Expand Down
8 changes: 8 additions & 0 deletions src/dataset/domain/models/Dataset.ts
Original file line number Diff line number Diff line change
Expand Up @@ -255,6 +255,12 @@ export class DatasetVersion {
public readonly isInReview: boolean,
public readonly latestVersionPublishingStatus: DatasetPublishingStatus,
public readonly someDatasetVersionHasBeenReleased: boolean,
/**
* The timestamp of the last update to this dataset version.
* Format: ISO 8601 string (e.g., "2023-06-01T12:34:56Z").
* Used for optimistic concurrency control to detect concurrent updates.
*/
public readonly lastUpdateTime: string,
public readonly termsOfAccess?: TermsOfAccess,
public readonly deaccessionNote?: string
) {}
Expand All @@ -271,6 +277,7 @@ export class DatasetVersion {
public readonly isInReview: boolean,
public readonly latestVersionPublishingStatus: DatasetPublishingStatus,
public readonly someDatasetVersionHasBeenReleased: boolean,
public readonly lastUpdateTime: string,
public readonly termsOfAccess?: TermsOfAccess,
public readonly deaccessionNote?: string
) {
Expand Down Expand Up @@ -339,6 +346,7 @@ export class DatasetVersion {
this.isInReview,
this.latestVersionPublishingStatus,
this.someDatasetVersionHasBeenReleased,
this.lastUpdateTime,
this.termsOfAccess,
this.deaccessionNote
)
Expand Down
2 changes: 1 addition & 1 deletion src/dataset/domain/repositories/DatasetRepository.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ export interface DatasetRepository {
updateMetadata: (
datasetId: string | number,
datasetDTO: DatasetDTO,
internalVersionNumber: number
sourceLastUpdateTime?: string
) => Promise<void>
deaccession: (
datasetId: string | number,
Expand Down
4 changes: 2 additions & 2 deletions src/dataset/domain/useCases/updateDatasetMetadata.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,10 @@ export function updateDatasetMetadata(
datasetRepository: DatasetRepository,
datasetId: string | number,
updatedDataset: DatasetDTO,
internalVersionNumber: number
sourceLastUpdateTime?: string
): Promise<void> {
return datasetRepository
.updateMetadata(datasetId, updatedDataset, internalVersionNumber)
.updateMetadata(datasetId, updatedDataset, sourceLastUpdateTime)
.catch((error: Error) => {
throw new Error(error.message)
})
Expand Down
1 change: 1 addition & 0 deletions src/dataset/infrastructure/mappers/JSDatasetMapper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ export class JSDatasetMapper {
jsDataset.versionInfo,
JSDatasetMapper.toDatasetTitle(jsDataset.metadataBlocks),
jsDatasetCitation,
jsDataset.versionInfo.lastUpdateTime,
jsDataset.publicationDate,
jsDataset.termsOfUse?.termsOfAccess,
jsDataset.versionInfo.deaccessionNote as string
Expand Down
3 changes: 2 additions & 1 deletion src/dataset/infrastructure/mappers/JSDatasetPreviewMapper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,8 @@ export class JSDatasetPreviewMapper {
jsDatasetPreview.versionId,
jsDatasetPreview.versionInfo,
jsDatasetPreview.title,
jsDatasetPreview.citation
jsDatasetPreview.citation,
jsDatasetPreview.versionInfo.lastUpdateTime
),
releaseOrCreateDate: JSDatasetPreviewMapper.toPreviewDate(jsDatasetPreview.versionInfo),
description: jsDatasetPreview.description,
Expand Down
2 changes: 2 additions & 0 deletions src/dataset/infrastructure/mappers/JSDatasetVersionMapper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ export class JSDatasetVersionMapper {
jsDatasetVersionInfo: JSDatasetVersionInfo,
jsDatasetTitle: string,
jsDatasetCitation: string,
jsDatasetLastUpdateTime: string,
jsDatasetPublicationDate?: string,
jsDatasettermsOfAccess?: TermsOfAccess,
jsDatasetDeaccessionedNote?: string
Expand All @@ -29,6 +30,7 @@ export class JSDatasetVersionMapper {
false, // TODO Connect with dataset version isInReview
this.toStatus(jsDatasetVersionInfo.state),
this.toSomeDatasetVersionHasBeenReleased(jsDatasetVersionInfo, jsDatasetPublicationDate),
jsDatasetLastUpdateTime,
jsDatasettermsOfAccess,
jsDatasetDeaccessionedNote
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -348,10 +348,10 @@ export class DatasetJSDataverseRepository implements DatasetRepository {
updateMetadata(
datasetId: string | number,
updatedDataset: DatasetDTO,
internalVersionNumber: number
sourceLastUpdateTime?: string
): Promise<void> {
return updateDataset
.execute(datasetId, DatasetDTOMapper.toJSDatasetDTO(updatedDataset), internalVersionNumber)
.execute(datasetId, DatasetDTOMapper.toJSDatasetDTO(updatedDataset), sourceLastUpdateTime)
.catch((error: WriteError) => {
throw new Error(error.message)
})
Expand Down
6 changes: 5 additions & 1 deletion src/files/domain/repositories/FileRepository.ts
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,11 @@ export interface FileRepository {
delete: (fileId: number | string) => Promise<void>
replace: (fileId: number | string, uploadedFileDTO: UploadedFileDTO) => Promise<number>
getFixityAlgorithm: () => Promise<FixityAlgorithm>
updateMetadata: (fileId: number | string, fileMetadata: FileMetadataDTO) => Promise<void>
updateMetadata: (
fileId: number | string,
fileMetadata: FileMetadataDTO,
sourceLastUpdateTime?: string
) => Promise<void>
restrict: (fileId: number | string, restrictFileDTO: RestrictFileDTO) => Promise<void>
updateTabularTags: (
fileId: number | string,
Expand Down
5 changes: 3 additions & 2 deletions src/files/domain/useCases/editFileMetadata.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@ import { FileMetadataDTO } from '@/files/domain/useCases/DTOs/FileMetadataDTO'
export function editFileMetadata(
fileRepository: FileRepository,
fileId: number | string,
fileMetadata: FileMetadataDTO
fileMetadata: FileMetadataDTO,
sourceLastUpdateTime?: string
): Promise<void> {
return fileRepository.updateMetadata(fileId, fileMetadata)
return fileRepository.updateMetadata(fileId, fileMetadata, sourceLastUpdateTime)
}
8 changes: 6 additions & 2 deletions src/files/infrastructure/FileJSDataverseRepository.ts
Original file line number Diff line number Diff line change
Expand Up @@ -356,8 +356,12 @@ export class FileJSDataverseRepository implements FileRepository {
.then((newFileIdentifier) => newFileIdentifier)
}

updateMetadata(fileId: number | string, fileMetadata: FileMetadataDTO): Promise<void> {
return updateFileMetadata.execute(fileId, fileMetadata)
updateMetadata(
fileId: number | string,
fileMetadata: FileMetadataDTO,
sourceLastUpdateTime?: string
): Promise<void> {
return updateFileMetadata.execute(fileId, fileMetadata, sourceLastUpdateTime)
}
// TODO - Not a priority but could be nice to implement this use case in js-dataverse when having time
getFixityAlgorithm(): Promise<FixityAlgorithm> {
Expand Down
1 change: 1 addition & 0 deletions src/files/infrastructure/mappers/JSFileMapper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ export class JSFileMapper {
jsDataset.versionInfo,
JSDatasetMapper.toDatasetTitle(jsDataset.metadataBlocks),
datasetCitation,
jsDataset.versionInfo.lastUpdateTime,
jsDataset.publicationDate,
jsDataset.termsOfUse?.termsOfAccess
)
Expand Down
2 changes: 1 addition & 1 deletion src/sections/edit-dataset-metadata/EditDatasetMetadata.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ export const EditDatasetMetadata = ({
metadataBlockInfoRepository={metadataBlockInfoRepository}
datasetPersistentID={dataset.persistentId}
datasetMetadaBlocksCurrentValues={dataset.metadataBlocks}
datasetInternalVersionNumber={dataset.internalVersionNumber}
datasetLastUpdateTime={dataset.version.lastUpdateTime}
/>
</div>
</Tabs.Tab>
Expand Down
1 change: 1 addition & 0 deletions src/sections/edit-file-metadata/EditFileMetadata.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@ export const EditFileMetadata = ({ fileId, fileRepository, referrer }: EditFileM
editFileMetadataFormData={createEditFileMetadataFormData(file)}
referrer={referrer}
datasetPersistentId={file.hierarchy.parent?.persistentId}
datasetLastUpdateTime={file.datasetVersion.lastUpdateTime}
/>
</div>
</Tabs.Tab>
Expand Down
7 changes: 5 additions & 2 deletions src/sections/edit-file-metadata/EditFilesList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -31,13 +31,15 @@ type EditFilesListProps = {
editFileMetadataFormData: EditFileMetadataFormData
referrer: EditFileMetadataReferrer
datasetPersistentId?: string
datasetLastUpdateTime: string
}

export const EditFilesList = ({
fileRepository,
editFileMetadataFormData,
referrer,
datasetPersistentId
datasetPersistentId,
datasetLastUpdateTime
}: EditFilesListProps) => {
const { t } = useTranslation('shared')
const navigate = useNavigate()
Expand All @@ -56,7 +58,8 @@ export const EditFilesList = ({
}
const { submitForm, submissionStatus, submitError } = useSubmitFileMetadata(
fileRepository,
onSubmitSucceed
onSubmitSucceed,
datasetLastUpdateTime
)
const isSaving = submissionStatus === SubmissionStatus.IsSubmitting
const handleCancel = () => navigate(-1)
Expand Down
5 changes: 3 additions & 2 deletions src/sections/edit-file-metadata/useSubmitFileMetadata.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,8 @@ type UseSubmitFileMetadataReturnType =

export const useSubmitFileMetadata = (
fileRepository: FileRepository,
onSubmitSucceed: () => void
onSubmitSucceed: () => void,
datasetLastUpdateTime: string
) => {
const [submissionStatus, setSubmissionStatus] = useState<SubmissionStatus>(
SubmissionStatus.NotSubmitted
Expand All @@ -45,7 +46,7 @@ export const useSubmitFileMetadata = (
directoryLabel: file.fileDir,
label: file.fileName
}
await editFileMetadata(fileRepository, file.id, fileMetadataDTO)
await editFileMetadata(fileRepository, file.id, fileMetadataDTO, datasetLastUpdateTime)
}

setSubmitError(null)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ interface FormProps {
errorLoadingMetadataBlocksInfo: string | null
datasetRepository: DatasetRepository
datasetPersistentID?: string
datasetInternalVersionNumber?: number
datasetLastUpdateTime?: string
}

export const MetadataForm = ({
Expand All @@ -35,7 +35,7 @@ export const MetadataForm = ({
errorLoadingMetadataBlocksInfo,
datasetRepository,
datasetPersistentID,
datasetInternalVersionNumber
datasetLastUpdateTime
}: FormProps) => {
const { user } = useSession()
const navigate = useNavigate()
Expand All @@ -57,7 +57,7 @@ export const MetadataForm = ({
datasetRepository,
onSubmitDatasetError,
datasetPersistentID,
datasetInternalVersionNumber
datasetLastUpdateTime
)

useEffect(() => {
Expand Down
8 changes: 4 additions & 4 deletions src/sections/shared/form/DatasetMetadataForm/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ type DatasetMetadataFormProps =
datasetPersistentID?: never
metadataBlockInfoRepository: MetadataBlockInfoRepository
datasetMetadaBlocksCurrentValues?: never
datasetInternalVersionNumber?: never
datasetLastUpdateTime?: never
}
| {
mode: 'edit'
Expand All @@ -26,7 +26,7 @@ type DatasetMetadataFormProps =
datasetPersistentID: string
metadataBlockInfoRepository: MetadataBlockInfoRepository
datasetMetadaBlocksCurrentValues: DatasetMetadataBlocks
datasetInternalVersionNumber: number
datasetLastUpdateTime?: string
}

export type DatasetMetadataFormMode = 'create' | 'edit'
Expand All @@ -38,7 +38,7 @@ export const DatasetMetadataForm = ({
datasetPersistentID,
metadataBlockInfoRepository,
datasetMetadaBlocksCurrentValues,
datasetInternalVersionNumber
datasetLastUpdateTime
}: DatasetMetadataFormProps) => {
const { setIsLoading } = useLoading()
const onEditMode = mode === 'edit'
Expand Down Expand Up @@ -115,7 +115,7 @@ export const DatasetMetadataForm = ({
errorLoadingMetadataBlocksInfo={errorLoadingMetadataBlocksInfo}
datasetRepository={datasetRepository}
datasetPersistentID={datasetPersistentID}
datasetInternalVersionNumber={datasetInternalVersionNumber}
datasetLastUpdateTime={datasetLastUpdateTime}
/>
)
}
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ export function useSubmitDataset(
datasetRepository: DatasetRepository,
onSubmitErrorCallback: () => void,
datasetPersistentID?: string,
datasetInternalVersionNumber?: number
datasetLastUpdateTime?: string
): UseSubmitDatasetReturnType {
const navigate = useNavigate()
const { t } = useTranslation('shared', { keyPrefix: 'datasetMetadataForm' })
Expand Down Expand Up @@ -89,7 +89,7 @@ export function useSubmitDataset(
datasetRepository,
currentEditedDatasetPersistentID,
formattedFormValues,
datasetInternalVersionNumber as number
datasetLastUpdateTime as string
)
.then(() => {
setSubmitError(null)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ export const EditMode: Story = {
metadataBlockInfoRepository={new MetadataBlockInfoMockRepository()}
datasetPersistentID={datasetToEditMock.persistentId}
datasetMetadaBlocksCurrentValues={datasetToEditMock.metadataBlocks}
datasetInternalVersionNumber={1}
datasetLastUpdateTime="2023-06-01T12:34:56Z"
/>
)
}
3 changes: 2 additions & 1 deletion tests/component/dataset/domain/models/DatasetMother.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,8 @@ export class DatasetVersionMother {
props?.isLatest ?? false,
props?.isInReview ?? false,
props?.latestVersionPublishingStatus ?? DatasetPublishingStatus.RELEASED,
props?.someDatasetVersionHasBeenReleased ?? faker.datatype.boolean()
props?.someDatasetVersionHasBeenReleased ?? faker.datatype.boolean(),
props?.lastUpdateTime ?? faker.date.recent().toISOString()
)
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ const jsDataset = {
majorNumber: 0,
minorNumber: 0,
createTime: new Date('2023-09-07T13:40:04.000Z'),
lastUpdateTime: new Date('2023-09-07T13:40:04.000Z'),
lastUpdateTime: '2023-09-07T13:40:04.000Z',
releaseTime: undefined,
deaccessionNote: undefined
},
Expand Down Expand Up @@ -231,6 +231,7 @@ const expectedDataset = {
majorNumber: 0
},
someDatasetVersionHasBeenReleased: false,
lastUpdateTime: '2023-09-07T13:40:04.000Z',
termsOfAccess: termsOfAccess,
deaccessionNote: undefined,
citation:
Expand Down Expand Up @@ -349,6 +350,7 @@ const expectedDatasetWithPublicationDate = {
majorNumber: 0
},
someDatasetVersionHasBeenReleased: true,
lastUpdateTime: '2023-09-07T13:40:04.000Z',
citation:
'Finch, Fiona, 2023, "Darwin\'s Finches", <a href="https://doi.org/10.5072/FK2/B4B2MJ" target="_blank">https://doi.org/10.5072/FK2/B4B2MJ</a>, Root, DRAFT VERSION'
},
Expand Down Expand Up @@ -464,6 +466,7 @@ const expectedDatasetWithNextVersionNumbers = {
majorNumber: 0
},
someDatasetVersionHasBeenReleased: true,
lastUpdateTime: '2023-09-07T13:40:04.000Z',
termsOfAccess: termsOfAccess,
deaccessionNote: undefined,
citation:
Expand Down Expand Up @@ -585,6 +588,7 @@ const expectedDatasetAlternateVersion = {
majorNumber: 0
},
someDatasetVersionHasBeenReleased: false,
lastUpdateTime: '2023-09-07T13:40:04.000Z',
termsOfAccess: termsOfAccess,
deaccessionNote: undefined,
citation:
Expand Down
Loading
Loading