diff --git a/package-lock.json b/package-lock.json index ca4054cb7..9e3668d9a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -13,7 +13,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.31", + "@iqss/dataverse-client-javascript": "2.0.0-alpha.33", "@iqss/dataverse-design-system": "*", "@istanbuljs/nyc-config-typescript": "1.0.2", "@tanstack/react-table": "8.9.2", @@ -3684,9 +3684,9 @@ }, "node_modules/@iqss/dataverse-client-javascript": { "name": "@IQSS/dataverse-client-javascript", - "version": "2.0.0-alpha.31", - "resolved": "https://npm.pkg.github.com/download/@IQSS/dataverse-client-javascript/2.0.0-alpha.31/c1ae5b3d55ee7e77d7c16670c1b7ca31934b08ce", - "integrity": "sha512-FAvnKp/p3v+ltiammkY93dpW/vdD/2XUYMp5RPpQyO3HQBvhZRYTTKc99qWpHCVr2f1UyTzg9bbOHoZPkpFLxw==", + "version": "2.0.0-alpha.33", + "resolved": "https://npm.pkg.github.com/download/@IQSS/dataverse-client-javascript/2.0.0-alpha.33/1978f8c7e1e1b584e5d53e3f3c29806e6adb9ef1", + "integrity": "sha512-R1XcmmQYQLzTsmh3QixzVvR3FxgHBCgbJmZAd8rEkIbWbr0I2Oo7fvO60gY7UWtMUsMtsNo0Ag7pF4VSZQiTxw==", "license": "MIT", "dependencies": { "@types/node": "^18.15.11", diff --git a/package.json b/package.json index c8d69e004..1d3abf876 100644 --- a/package.json +++ b/package.json @@ -17,7 +17,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.31", + "@iqss/dataverse-client-javascript": "2.0.0-alpha.33", "@iqss/dataverse-design-system": "*", "@istanbuljs/nyc-config-typescript": "1.0.2", "@tanstack/react-table": "8.9.2", diff --git a/src/dataset/domain/models/Dataset.ts b/src/dataset/domain/models/Dataset.ts index ad2981fed..91aa9e784 100644 --- a/src/dataset/domain/models/Dataset.ts +++ b/src/dataset/domain/models/Dataset.ts @@ -401,6 +401,7 @@ export class Dataset { constructor( public readonly persistentId: string, public readonly version: DatasetVersion, + public readonly internalVersionNumber: number, public readonly alerts: Alert[], public readonly summaryFields: DatasetMetadataBlock[], public readonly termsOfUse: DatasetTermsOfUse, @@ -496,6 +497,7 @@ export class Dataset { constructor( public readonly persistentId: string, public readonly version: DatasetVersion, + public readonly internalVersionNumber: number, public readonly summaryFields: DatasetMetadataBlock[], public readonly termsOfUse: DatasetTermsOfUse, public readonly metadataBlocks: DatasetMetadataBlocks, @@ -565,6 +567,7 @@ export class Dataset { return new Dataset( this.persistentId, this.version, + this.internalVersionNumber, this.alerts, this.summaryFields, this.termsOfUse, diff --git a/src/dataset/domain/repositories/DatasetRepository.ts b/src/dataset/domain/repositories/DatasetRepository.ts index c50a6d147..9634a2a4b 100644 --- a/src/dataset/domain/repositories/DatasetRepository.ts +++ b/src/dataset/domain/repositories/DatasetRepository.ts @@ -22,7 +22,11 @@ export interface DatasetRepository { ) => Promise create: (dataset: DatasetDTO, collectionId: string) => Promise<{ persistentId: string }> - updateMetadata: (datasetId: string | number, datasetDTO: DatasetDTO) => Promise + updateMetadata: ( + datasetId: string | number, + datasetDTO: DatasetDTO, + internalVersionNumber: number + ) => Promise deaccession: ( datasetId: string | number, version: string, diff --git a/src/dataset/domain/useCases/updateDatasetMetadata.ts b/src/dataset/domain/useCases/updateDatasetMetadata.ts index 2f661c31f..d3a6c43c2 100644 --- a/src/dataset/domain/useCases/updateDatasetMetadata.ts +++ b/src/dataset/domain/useCases/updateDatasetMetadata.ts @@ -4,9 +4,12 @@ import { DatasetDTO } from './DTOs/DatasetDTO' export function updateDatasetMetadata( datasetRepository: DatasetRepository, datasetId: string | number, - updatedDataset: DatasetDTO + updatedDataset: DatasetDTO, + internalVersionNumber: number ): Promise { - return datasetRepository.updateMetadata(datasetId, updatedDataset).catch((error: Error) => { - throw new Error(error.message) - }) + return datasetRepository + .updateMetadata(datasetId, updatedDataset, internalVersionNumber) + .catch((error: Error) => { + throw new Error(error.message) + }) } diff --git a/src/dataset/infrastructure/mappers/JSDatasetMapper.ts b/src/dataset/infrastructure/mappers/JSDatasetMapper.ts index ca7daa73e..2fd6aa81e 100644 --- a/src/dataset/infrastructure/mappers/JSDatasetMapper.ts +++ b/src/dataset/infrastructure/mappers/JSDatasetMapper.ts @@ -61,6 +61,7 @@ export class JSDatasetMapper { return new Dataset.Builder( jsDataset.persistentId, version, + jsDataset.internalVersionNumber, JSDatasetMapper.toSummaryFields(jsDataset.metadataBlocks, jsDatasetSummaryFieldsNames), jsDataset.termsOfUse, JSDatasetMapper.toMetadataBlocks( diff --git a/src/dataset/infrastructure/repositories/DatasetJSDataverseRepository.ts b/src/dataset/infrastructure/repositories/DatasetJSDataverseRepository.ts index 00da3cc56..65132a6ac 100644 --- a/src/dataset/infrastructure/repositories/DatasetJSDataverseRepository.ts +++ b/src/dataset/infrastructure/repositories/DatasetJSDataverseRepository.ts @@ -317,9 +317,13 @@ export class DatasetJSDataverseRepository implements DatasetRepository { }) } - updateMetadata(datasetId: string | number, updatedDataset: DatasetDTO): Promise { + updateMetadata( + datasetId: string | number, + updatedDataset: DatasetDTO, + internalVersionNumber: number + ): Promise { return updateDataset - .execute(datasetId, DatasetDTOMapper.toJSDatasetDTO(updatedDataset)) + .execute(datasetId, DatasetDTOMapper.toJSDatasetDTO(updatedDataset), internalVersionNumber) .catch((error: WriteError) => { throw new Error(error.message) }) diff --git a/src/sections/edit-dataset-metadata/EditDatasetMetadata.tsx b/src/sections/edit-dataset-metadata/EditDatasetMetadata.tsx index 8dfc077e7..8242096c6 100644 --- a/src/sections/edit-dataset-metadata/EditDatasetMetadata.tsx +++ b/src/sections/edit-dataset-metadata/EditDatasetMetadata.tsx @@ -70,6 +70,7 @@ export const EditDatasetMetadata = ({ metadataBlockInfoRepository={metadataBlockInfoRepository} datasetPersistentID={dataset.persistentId} datasetMetadaBlocksCurrentValues={dataset.metadataBlocks} + datasetInternalVersionNumber={dataset.internalVersionNumber} /> diff --git a/src/sections/shared/form/DatasetMetadataForm/MetadataFieldsHelper.ts b/src/sections/shared/form/DatasetMetadataForm/MetadataFieldsHelper.ts index 0d0f23ef7..b5d6bab9e 100644 --- a/src/sections/shared/form/DatasetMetadataForm/MetadataFieldsHelper.ts +++ b/src/sections/shared/form/DatasetMetadataForm/MetadataFieldsHelper.ts @@ -298,7 +298,10 @@ export class MetadataFieldsHelper { return formattedNewObject } - public static formatFormValuesToDatasetDTO(formValues: DatasetMetadataFormValues): DatasetDTO { + public static formatFormValuesToDatasetDTO( + formValues: DatasetMetadataFormValues, + mode: 'create' | 'edit' + ): DatasetDTO { const metadataBlocks: DatasetDTO['metadataBlocks'] = [] for (const metadataBlockName in formValues) { @@ -310,14 +313,14 @@ export class MetadataFieldsHelper { Object.entries(metadataBlockFormValues).forEach(([fieldName, fieldValue]) => { if (this.isPrimitiveFieldValue(fieldValue)) { - if (fieldValue !== '') { + if (fieldValue !== '' || mode === 'edit') { formattedMetadataBlock.fields[fieldName] = fieldValue return } return } if (this.isVocabularyMultipleFieldValue(fieldValue)) { - if (fieldValue.length > 0) { + if (fieldValue.length > 0 || mode === 'edit') { formattedMetadataBlock.fields[fieldName] = fieldValue return } @@ -329,7 +332,7 @@ export class MetadataFieldsHelper { .map((primitiveField) => primitiveField.value) .filter((v) => v !== '') - if (primitiveMultipleFieldValues.length > 0) { + if (primitiveMultipleFieldValues.length > 0 || mode === 'edit') { formattedMetadataBlock.fields[fieldName] = primitiveMultipleFieldValues return } @@ -340,7 +343,7 @@ export class MetadataFieldsHelper { const formattedMetadataChildFieldValue: DatasetMetadataChildFieldValueDTO = {} Object.entries(fieldValue).forEach(([nestedFieldName, nestedFieldValue]) => { - if (nestedFieldValue !== '') { + if (nestedFieldValue !== '' || mode === 'edit') { formattedMetadataChildFieldValue[nestedFieldName] = nestedFieldValue } }) @@ -357,15 +360,15 @@ export class MetadataFieldsHelper { fieldValue.forEach((composedFieldValues) => { const composedField: DatasetMetadataChildFieldValueDTO = {} Object.entries(composedFieldValues).forEach(([nestedFieldName, nestedFieldValue]) => { - if (nestedFieldValue !== '') { + if (nestedFieldValue !== '' || mode === 'edit') { composedField[nestedFieldName] = nestedFieldValue } }) - if (Object.keys(composedField).length > 0) { + if (Object.keys(composedField).length > 0 || mode === 'edit') { formattedMetadataChildFieldValues.push(composedField) } }) - if (formattedMetadataChildFieldValues.length > 0) { + if (formattedMetadataChildFieldValues.length > 0 || mode === 'edit') { formattedMetadataBlock.fields[fieldName] = formattedMetadataChildFieldValues } diff --git a/src/sections/shared/form/DatasetMetadataForm/MetadataForm/index.tsx b/src/sections/shared/form/DatasetMetadataForm/MetadataForm/index.tsx index 34a66581d..0a6f4d5eb 100644 --- a/src/sections/shared/form/DatasetMetadataForm/MetadataForm/index.tsx +++ b/src/sections/shared/form/DatasetMetadataForm/MetadataForm/index.tsx @@ -23,6 +23,7 @@ interface FormProps { errorLoadingMetadataBlocksInfo: string | null datasetRepository: DatasetRepository datasetPersistentID?: string + datasetInternalVersionNumber?: number } export const MetadataForm = ({ @@ -32,7 +33,8 @@ export const MetadataForm = ({ metadataBlocksInfo, errorLoadingMetadataBlocksInfo, datasetRepository, - datasetPersistentID + datasetPersistentID, + datasetInternalVersionNumber }: FormProps) => { const { user } = useSession() const navigate = useNavigate() @@ -53,7 +55,8 @@ export const MetadataForm = ({ collectionId, datasetRepository, onSubmitDatasetError, - datasetPersistentID + datasetPersistentID, + datasetInternalVersionNumber ) useEffect(() => { diff --git a/src/sections/shared/form/DatasetMetadataForm/index.tsx b/src/sections/shared/form/DatasetMetadataForm/index.tsx index 4f041f319..4ebd7aaab 100644 --- a/src/sections/shared/form/DatasetMetadataForm/index.tsx +++ b/src/sections/shared/form/DatasetMetadataForm/index.tsx @@ -17,6 +17,7 @@ type DatasetMetadataFormProps = datasetPersistentID?: never metadataBlockInfoRepository: MetadataBlockInfoRepository datasetMetadaBlocksCurrentValues?: never + datasetInternalVersionNumber?: never } | { mode: 'edit' @@ -25,6 +26,7 @@ type DatasetMetadataFormProps = datasetPersistentID: string metadataBlockInfoRepository: MetadataBlockInfoRepository datasetMetadaBlocksCurrentValues: DatasetMetadataBlocks + datasetInternalVersionNumber: number } export type DatasetMetadataFormMode = 'create' | 'edit' @@ -35,7 +37,8 @@ export const DatasetMetadataForm = ({ datasetRepository, datasetPersistentID, metadataBlockInfoRepository, - datasetMetadaBlocksCurrentValues + datasetMetadaBlocksCurrentValues, + datasetInternalVersionNumber }: DatasetMetadataFormProps) => { const { setIsLoading } = useLoading() const onEditMode = mode === 'edit' @@ -112,6 +115,7 @@ export const DatasetMetadataForm = ({ errorLoadingMetadataBlocksInfo={errorLoadingMetadataBlocksInfo} datasetRepository={datasetRepository} datasetPersistentID={datasetPersistentID} + datasetInternalVersionNumber={datasetInternalVersionNumber} /> ) } diff --git a/src/sections/shared/form/DatasetMetadataForm/useSubmitDataset.ts b/src/sections/shared/form/DatasetMetadataForm/useSubmitDataset.ts index ae27e0d2d..04efccdb7 100644 --- a/src/sections/shared/form/DatasetMetadataForm/useSubmitDataset.ts +++ b/src/sections/shared/form/DatasetMetadataForm/useSubmitDataset.ts @@ -37,7 +37,8 @@ export function useSubmitDataset( collectionId: string, datasetRepository: DatasetRepository, onSubmitErrorCallback: () => void, - datasetPersistentID?: string + datasetPersistentID?: string, + datasetInternalVersionNumber?: number ): UseSubmitDatasetReturnType { const navigate = useNavigate() const { t } = useTranslation('shared', { keyPrefix: 'datasetMetadataForm' }) @@ -53,7 +54,8 @@ export function useSubmitDataset( const formDataBackToOriginalKeys = MetadataFieldsHelper.replaceSlashKeysWithDot(formData) const formattedFormValues = MetadataFieldsHelper.formatFormValuesToDatasetDTO( - formDataBackToOriginalKeys + formDataBackToOriginalKeys, + mode ) if (mode === 'create') { @@ -86,7 +88,8 @@ export function useSubmitDataset( updateDatasetMetadata( datasetRepository, currentEditedDatasetPersistentID, - formattedFormValues + formattedFormValues, + datasetInternalVersionNumber as number ) .then(() => { setSubmitError(null) diff --git a/src/stories/shared/dataset-metadata-form/DatasetMetadataForm.stories.tsx b/src/stories/shared/dataset-metadata-form/DatasetMetadataForm.stories.tsx index ffc54694f..367e0bf2c 100644 --- a/src/stories/shared/dataset-metadata-form/DatasetMetadataForm.stories.tsx +++ b/src/stories/shared/dataset-metadata-form/DatasetMetadataForm.stories.tsx @@ -40,6 +40,7 @@ export const EditMode: Story = { metadataBlockInfoRepository={new MetadataBlockInfoMockRepository()} datasetPersistentID={datasetToEditMock.persistentId} datasetMetadaBlocksCurrentValues={datasetToEditMock.metadataBlocks} + datasetInternalVersionNumber={1} /> ) } diff --git a/tests/component/dataset/domain/models/DatasetMother.ts b/tests/component/dataset/domain/models/DatasetMother.ts index fe55fd5c1..f59d23dce 100644 --- a/tests/component/dataset/domain/models/DatasetMother.ts +++ b/tests/component/dataset/domain/models/DatasetMother.ts @@ -339,6 +339,7 @@ export class DatasetMother { const dataset = { persistentId: faker.datatype.uuid(), version: DatasetVersionMother.create(), + internalVersionNumber: faker.datatype.number(), license: { name: 'CC0 1.0', uri: 'https://creativecommons.org/publicdomain/zero/1.0/', @@ -425,6 +426,7 @@ export class DatasetMother { return new Dataset.Builder( dataset.persistentId, dataset.version, + dataset.internalVersionNumber, dataset.summaryFields, dataset.termsOfUse, dataset.metadataBlocks, diff --git a/tests/component/dataset/infrastructure/mappers/JSDatasetMapper.spec.ts b/tests/component/dataset/infrastructure/mappers/JSDatasetMapper.spec.ts index dfb256065..c13b08d74 100644 --- a/tests/component/dataset/infrastructure/mappers/JSDatasetMapper.spec.ts +++ b/tests/component/dataset/infrastructure/mappers/JSDatasetMapper.spec.ts @@ -37,6 +37,7 @@ const jsDataset = { lastUpdateTime: new Date('2023-09-07T13:40:04.000Z'), releaseTime: undefined }, + internalVersionNumber: 1, termsOfUse: { termsOfAccess: { fileAccessRequest: true, @@ -245,6 +246,7 @@ const expectedDataset = { citation: 'Finch, Fiona, 2023, "Darwin\'s Finches", https://doi.org/10.5072/FK2/B4B2MJ, Root, DRAFT VERSION' }, + internalVersionNumber: 1, requestedVersion: undefined, publicationDate: undefined, alerts: [{ variant: 'warning', messageKey: 'draftVersion', dynamicFields: undefined }], @@ -385,6 +387,7 @@ const expectedDatasetWithPublicationDate = { citation: 'Finch, Fiona, 2023, "Darwin\'s Finches", https://doi.org/10.5072/FK2/B4B2MJ, Root, DRAFT VERSION' }, + internalVersionNumber: 1, requestedVersion: undefined, publicationDate: undefined, alerts: [{ variant: 'warning', messageKey: 'draftVersion', dynamicFields: undefined }], @@ -526,6 +529,7 @@ const expectedDatasetWithNextVersionNumbers = { citation: 'Finch, Fiona, 2023, "Darwin\'s Finches", https://doi.org/10.5072/FK2/B4B2MJ, Root, DRAFT VERSION' }, + internalVersionNumber: 1, requestedVersion: undefined, publicationDate: undefined, alerts: [{ variant: 'warning', messageKey: 'draftVersion', dynamicFields: undefined }], @@ -671,6 +675,7 @@ const expectedDatasetAlternateVersion = { citation: 'Finch, Fiona, 2023, "Darwin\'s Finches", https://doi.org/10.5072/FK2/B4B2MJ, Root, DRAFT VERSION' }, + internalVersionNumber: 1, requestedVersion: '4.0', publicationDate: undefined, hasValidTermsOfAccess: true, diff --git a/tests/component/sections/shared/dataset-metadata-form/MetadataFieldsHelper.spec.ts b/tests/component/sections/shared/dataset-metadata-form/MetadataFieldsHelper.spec.ts index fd19ce995..f36564fd7 100644 --- a/tests/component/sections/shared/dataset-metadata-form/MetadataFieldsHelper.spec.ts +++ b/tests/component/sections/shared/dataset-metadata-form/MetadataFieldsHelper.spec.ts @@ -1247,7 +1247,7 @@ const formValuesWithValuesBackToDotKeys: DatasetMetadataFormValues = { } } -const expectedDatasetDTO: DatasetDTO = { +const expectedDatasetDTOInCreateMode: DatasetDTO = { licence: defaultLicense, metadataBlocks: [ { @@ -1281,6 +1281,43 @@ const expectedDatasetDTO: DatasetDTO = { ] } +const expectedDatasetDTOInEditMode: DatasetDTO = { + licence: defaultLicense, + metadataBlocks: [ + { + name: 'foo', + fields: { + someKeyWithoutDot: 'bar', + controlledVocabularyNotMultiple: 'Option2', + controlledVocabularyMultiple: ['Option1'], + 'primitive.text.not.multiple': 'foo', + 'primitive.text.multiple': ['foo', 'bar'], + 'primitive.textbox.not.multiple': '', + 'primitive.textbox.multiple': [], + 'primitive.float.not.multiple': '23.55', + 'primitive.float.multiple': ['23.55', '45.55'], + 'primitive.int.not.multiple': '23', + 'primitive.int.multiple': ['23', '45'], + 'primitive.date.not.multiple': '2022-01-01', + 'primitive.date.multiple': ['2022-01-01', '2022-12-31'], + 'composed.field.multiple': [ + { + 'subfield.1': 'foo', + 'subfield.2': 'bar', + someNestedKeyWithoutDot: 'bar', + controlledVocabularyNotMultiple: '' + } + ], + 'composed.field.not.multiple': { + 'subfield.1': 'foo', + 'subfield.2': 'bar', + someNestedKeyWithoutDot: 'bar' + } + } + } + ] +} + describe('MetadataFieldsHelper', () => { it('should replace names keys with dots with slashes from a metadata block info', () => { const result = @@ -1322,12 +1359,24 @@ describe('MetadataFieldsHelper', () => { expect(result).to.deep.equal(formValuesWithValuesBackToDotKeys) }) - it('should get dataset DTO from form values', () => { - const result = MetadataFieldsHelper.formatFormValuesToDatasetDTO( - formValuesWithValuesBackToDotKeys - ) + describe('should format form values to dataset DTO', () => { + it('In create mode, removing empty values', () => { + const result = MetadataFieldsHelper.formatFormValuesToDatasetDTO( + formValuesWithValuesBackToDotKeys, + 'create' + ) - expect(result).to.deep.equal(expectedDatasetDTO) + expect(result).to.deep.equal(expectedDatasetDTOInCreateMode) + }) + + it('In edit mode, keeping empty values', () => { + const result = MetadataFieldsHelper.formatFormValuesToDatasetDTO( + formValuesWithValuesBackToDotKeys, + 'edit' + ) + + expect(result).to.deep.equal(expectedDatasetDTOInEditMode) + }) }) it('works all together, simulates a use in an edit mode form', () => { @@ -1353,7 +1402,8 @@ describe('MetadataFieldsHelper', () => { MetadataFieldsHelper.replaceSlashKeysWithDot(inTestFormDefaultValues) const inTestDatasetDTO = MetadataFieldsHelper.formatFormValuesToDatasetDTO( - formValuesWithValuesBackToDotKeys + formValuesWithValuesBackToDotKeys, + 'edit' ) expect(inTestNormalizedMetadataBlocksInfo).to.deep.equal(normalizedMetadataBlocksInfo) @@ -1367,7 +1417,7 @@ describe('MetadataFieldsHelper', () => { expect(inTestFormDefaultValuesBackToDotKeys).to.deep.equal(formValuesWithValuesBackToDotKeys) - expect(inTestDatasetDTO).to.deep.equal(expectedDatasetDTO) + expect(inTestDatasetDTO).to.deep.equal(expectedDatasetDTOInEditMode) }) describe('defineFieldName', () => {