Skip to content

Commit 2e4fd4c

Browse files
authored
Merge pull request #7337 from QualitativeDataRepository/IQSS/7149-Replace_file_in_draft
Allow replacing a draft file
2 parents 7ba0fd7 + 518e9ee commit 2e4fd4c

13 files changed

Lines changed: 317 additions & 207 deletions

File tree

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
## Major Use Cases
2+
3+
- Users can now replace files in draft datasets. This functionality was previously only available on published datasets. Issue #7149/PR #7337

src/main/java/edu/harvard/iq/dataverse/EditDatafilesPage.java

Lines changed: 10 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,8 @@
3636
import edu.harvard.iq.dataverse.util.SystemConfig;
3737
import edu.harvard.iq.dataverse.util.BundleUtil;
3838
import edu.harvard.iq.dataverse.util.EjbUtil;
39+
import edu.harvard.iq.dataverse.util.FileMetadataUtil;
40+
3941
import static edu.harvard.iq.dataverse.util.JsfHelper.JH;
4042
import java.io.File;
4143
import java.io.FileOutputStream;
@@ -796,8 +798,12 @@ private void deleteFiles(List<FileMetadata> filesForDelete) {
796798
// and let the delete be handled in the command (by adding it to the
797799
// filesToBeDeleted list):
798800

801+
// ToDo - FileMetadataUtil.removeFileMetadataFromList should handle these two
802+
// removes so they could be put after this if clause and the else clause could
803+
// be removed.
799804
dataset.getEditVersion().getFileMetadatas().remove(markedForDelete);
800805
fileMetadatas.remove(markedForDelete);
806+
801807
filesToBeDeleted.add(markedForDelete);
802808
} else {
803809
logger.fine("this is a brand-new (unsaved) filemetadata");
@@ -810,9 +816,9 @@ private void deleteFiles(List<FileMetadata> filesForDelete) {
810816
// fileMetadatas list. (but doing both just adds a no-op and won't cause an
811817
// error)
812818
// 1. delete the filemetadata from the local display list:
813-
removeFileMetadataFromList(fileMetadatas, markedForDelete);
819+
FileMetadataUtil.removeFileMetadataFromList(fileMetadatas, markedForDelete);
814820
// 2. delete the filemetadata from the version:
815-
removeFileMetadataFromList(dataset.getEditVersion().getFileMetadatas(), markedForDelete);
821+
FileMetadataUtil.removeFileMetadataFromList(dataset.getEditVersion().getFileMetadatas(), markedForDelete);
816822
}
817823

818824
if (markedForDelete.getDataFile().getId() == null) {
@@ -821,8 +827,8 @@ private void deleteFiles(List<FileMetadata> filesForDelete) {
821827
// removing it from the fileMetadatas lists (above), we also remove it from
822828
// the newFiles list and the dataset's files, so it never gets saved.
823829

824-
removeDataFileFromList(dataset.getFiles(), markedForDelete.getDataFile());
825-
removeDataFileFromList(newFiles, markedForDelete.getDataFile());
830+
FileMetadataUtil.removeDataFileFromList(dataset.getFiles(), markedForDelete.getDataFile());
831+
FileMetadataUtil.removeDataFileFromList(newFiles, markedForDelete.getDataFile());
826832
FileUtil.deleteTempFile(markedForDelete.getDataFile(), dataset, ingestService);
827833
// Also remove checksum from the list of newly uploaded checksums (perhaps odd
828834
// to delete and then try uploading the same file again, but it seems like it
@@ -851,28 +857,6 @@ private void deleteFiles(List<FileMetadata> filesForDelete) {
851857
}
852858

853859

854-
private void removeFileMetadataFromList(List<FileMetadata> fmds, FileMetadata fmToDelete) {
855-
Iterator<FileMetadata> fmit = fmds.iterator();
856-
while (fmit.hasNext()) {
857-
FileMetadata fmd = fmit.next();
858-
if (fmToDelete.getDataFile().getStorageIdentifier().equals(fmd.getDataFile().getStorageIdentifier())) {
859-
fmit.remove();
860-
break;
861-
}
862-
}
863-
}
864-
865-
private void removeDataFileFromList(List<DataFile> dfs, DataFile dfToDelete) {
866-
Iterator<DataFile> dfit = dfs.iterator();
867-
while (dfit.hasNext()) {
868-
DataFile df = dfit.next();
869-
if (dfToDelete.getStorageIdentifier().equals(df.getStorageIdentifier())) {
870-
dfit.remove();
871-
break;
872-
}
873-
}
874-
}
875-
876860

877861

878862
/**

src/main/java/edu/harvard/iq/dataverse/api/Datasets.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1834,6 +1834,9 @@ public Response addFileToDataset(@PathParam("id") String idSupplied,
18341834
} catch (DataFileTagException ex) {
18351835
return error( Response.Status.BAD_REQUEST, ex.getMessage());
18361836
}
1837+
catch (ClassCastException | com.google.gson.JsonParseException ex) {
1838+
return error(Response.Status.BAD_REQUEST, BundleUtil.getStringFromBundle("file.addreplace.error.parsing"));
1839+
}
18371840

18381841
// -------------------------------------
18391842
// (3) Get the file name and content type

src/main/java/edu/harvard/iq/dataverse/api/Files.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -212,8 +212,8 @@ public Response replaceFileInDataset(
212212
} catch (DataFileTagException ex) {
213213
return error(Response.Status.BAD_REQUEST, ex.getMessage());
214214
}
215-
} catch (ClassCastException ex) {
216-
logger.info("Exception parsing string '" + jsonData + "': " + ex);
215+
} catch (ClassCastException | com.google.gson.JsonParseException ex) {
216+
return error(Response.Status.BAD_REQUEST, BundleUtil.getStringFromBundle("file.addreplace.error.parsing"));
217217
}
218218
}
219219

@@ -357,7 +357,7 @@ public Response updateFileMetadata(@FormDataParam("jsonData") String jsonData,
357357
return error(Response.Status.BAD_REQUEST, ex.getMessage());
358358
}
359359
} catch (ClassCastException | com.google.gson.JsonParseException ex) {
360-
return error(Response.Status.BAD_REQUEST, "Exception parsing provided json");
360+
return error(Response.Status.BAD_REQUEST, BundleUtil.getStringFromBundle("file.addreplace.error.parsing"));
361361
}
362362
}
363363

src/main/java/edu/harvard/iq/dataverse/datasetutility/AddReplaceFileHelper.java

Lines changed: 81 additions & 115 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
import edu.harvard.iq.dataverse.engine.command.exception.CommandException;
2222
import edu.harvard.iq.dataverse.engine.command.impl.AbstractCreateDatasetCommand;
2323
import edu.harvard.iq.dataverse.engine.command.impl.CreateNewDatasetCommand;
24+
import edu.harvard.iq.dataverse.engine.command.impl.DeleteDataFileCommand;
2425
import edu.harvard.iq.dataverse.engine.command.impl.RestrictFileCommand;
2526
import edu.harvard.iq.dataverse.engine.command.impl.UpdateDatasetVersionCommand;
2627
import edu.harvard.iq.dataverse.ingest.IngestServiceBean;
@@ -504,6 +505,18 @@ public boolean runReplaceFromUI_Phase1(Long oldFileId,
504505
if (!this.step_005_loadFileToReplaceById(oldFileId)){
505506
return false;
506507
}
508+
//Update params to match existing file (except checksum, which should match the new file)
509+
if(fileToReplace != null) {
510+
String checksum = optionalFileParams.getCheckSum();
511+
ChecksumType checkSumType = optionalFileParams.getCheckSumType();
512+
try {
513+
optionalFileParams = new OptionalFileParams(fileToReplace);
514+
optionalFileParams.setCheckSum(checksum, checkSumType);
515+
} catch (DataFileTagException e) {
516+
// Shouldn't happen since fileToReplace should have valid tags
517+
e.printStackTrace();
518+
}
519+
}
507520

508521
return this.runAddReplacePhase1(fileToReplace.getOwner(),
509522
newFileName,
@@ -574,6 +587,26 @@ private boolean runAddReplacePhase1(Dataset owner,
574587
return false;
575588
}
576589

590+
// if the fileToReplace hasn't been released,
591+
if (fileToReplace != null && !fileToReplace.isReleased()) {
592+
DataFile df = finalFileList.get(0); // step_055 uses a loop and assumes only one file
593+
// set the replacement file's previous and root datafileIds to match (unless
594+
// they are the defaults)
595+
if (fileToReplace.getPreviousDataFileId() != null) {
596+
df.setPreviousDataFileId(fileToReplace.getPreviousDataFileId());
597+
df.setRootDataFileId(fileToReplace.getRootDataFileId());
598+
}
599+
// Reuse any file PID during a replace operation (if File PIDs are in use)
600+
if (systemConfig.isFilePIDsEnabled()) {
601+
df.setGlobalId(fileToReplace.getGlobalId());
602+
df.setGlobalIdCreateTime(fileToReplace.getGlobalIdCreateTime());
603+
// Should be true or fileToReplace wouldn't have an identifier (since it's not
604+
// yet released in this if statement)
605+
df.setIdentifierRegistered(fileToReplace.isIdentifierRegistered());
606+
fileToReplace.setGlobalId(null);
607+
}
608+
}
609+
577610
return true;
578611
}
579612

@@ -1061,16 +1094,6 @@ private boolean step_005_loadFileToReplaceById(Long dataFileId){
10611094
if (!step_015_auto_check_permissions(existingFile.getOwner())){
10621095
return false;
10631096
};
1064-
1065-
1066-
1067-
// Is the file published?
1068-
//
1069-
if (!existingFile.isReleased()){
1070-
addError(getBundleErr("unpublished_file_cannot_be_replaced"));
1071-
return false;
1072-
}
1073-
10741097
// Is the file in the latest dataset version?
10751098
//
10761099
if (!step_007_auto_isReplacementInLatestVersion(existingFile)){
@@ -1532,7 +1555,22 @@ private boolean step_070_run_update_dataset_command(){
15321555
}
15331556

15341557
Command<Dataset> update_cmd;
1535-
update_cmd = new UpdateDatasetVersionCommand(dataset, dvRequest, clone);
1558+
String deleteStorageLocation = null;
1559+
long deleteFileId=-1;
1560+
if(isFileReplaceOperation()) {
1561+
List<FileMetadata> filesToDelete = new ArrayList<FileMetadata>();
1562+
filesToDelete.add(fileToReplace.getFileMetadata());
1563+
1564+
if(!fileToReplace.isReleased()) {
1565+
//If file is only in draft version, also need to delete the physical file
1566+
deleteStorageLocation = fileService.getPhysicalFileToDelete(fileToReplace);
1567+
deleteFileId=fileToReplace.getId();
1568+
}
1569+
//Adding the file to the delete list for the command will delete this filemetadata and, if the file hasn't been released, the datafile itself.
1570+
update_cmd = new UpdateDatasetVersionCommand(dataset, dvRequest, filesToDelete, clone);
1571+
} else {
1572+
update_cmd = new UpdateDatasetVersionCommand(dataset, dvRequest, clone);
1573+
}
15361574
((UpdateDatasetVersionCommand) update_cmd).setValidateLenient(true);
15371575

15381576
try {
@@ -1554,89 +1592,23 @@ private boolean step_070_run_update_dataset_command(){
15541592
this.addErrorSevere("add.add_file_error (see logs)");
15551593
logger.severe(ex.getMessage());
15561594
return false;
1557-
}
1558-
return true;
1559-
}
1560-
1561-
1562-
/**
1563-
* Go through the working DatasetVersion and remove the
1564-
* FileMetadata of the file to replace
1565-
*
1566-
* @return
1567-
*/
1568-
private boolean step_085_auto_remove_filemetadata_to_replace_from_working_version(){
1569-
1570-
msgt("step_085_auto_remove_filemetadata_to_replace_from_working_version 1");
1571-
1572-
if (!isFileReplaceOperation()){
1573-
// Shouldn't happen!
1574-
this.addErrorSevere(getBundleErr("only_replace_operation") + " (step_085_auto_remove_filemetadata_to_replace_from_working_version");
1575-
return false;
1576-
}
1577-
msg("step_085_auto_remove_filemetadata_to_replace_from_working_version 2");
1578-
1579-
if (this.hasError()){
1580-
return false;
15811595
}
1582-
1583-
1584-
msgt("File to replace getId: " + fileToReplace.getId());
1585-
1586-
Iterator<FileMetadata> fmIt = workingVersion.getFileMetadatas().iterator();
1587-
msgt("Clear file to replace");
1588-
int cnt = 0;
1589-
while (fmIt.hasNext()) {
1590-
cnt++;
1591-
1592-
FileMetadata fm = fmIt.next();
1593-
msg(cnt + ") next file: " + fm);
1594-
msg(" getDataFile().getId(): " + fm.getDataFile().getId());
1595-
if (fm.getDataFile().getId() != null) {
1596-
if (Objects.equals(fm.getDataFile().getId(), fileToReplace.getId())) {
1597-
msg("Let's remove it!");
1598-
1599-
// If this is a tabular data file with a UNF, we'll need
1600-
// to recalculate the version UNF, once the file is removed:
1601-
1602-
boolean recalculateUNF = !StringUtils.isEmpty(fm.getDataFile().getUnf());
1603-
1604-
if (workingVersion.getId() != null) {
1605-
// If this is an existing draft (i.e., this draft version
1606-
// is already saved in the dataset, we'll also need to remove this filemetadata
1607-
// explicitly:
1608-
msg(" this is an existing draft version...");
1609-
fileService.removeFileMetadata(fm);
1610-
1611-
// remove the filemetadata from the list of filemetadatas
1612-
// attached to the datafile object as well, for a good
1613-
// measure:
1614-
fileToReplace.getFileMetadatas().remove(fm);
1615-
// (and yes, we can do .remove(fm) safely - if this released
1616-
// file is part of an existing draft, we know that the
1617-
// filemetadata object also exists in the database, and thus
1618-
// has the id, and can be identified unambiguously.
1619-
}
1620-
1621-
// and remove it from the list of filemetadatas attached
1622-
// to the version object, via the iterator:
1623-
fmIt.remove();
1624-
1625-
if (recalculateUNF) {
1626-
msg("recalculating the UNF");
1627-
ingestService.recalculateDatasetVersionUNF(workingVersion);
1628-
msg("UNF recalculated: "+workingVersion.getUNF());
1629-
}
1630-
1631-
return true;
1596+
//Sanity check
1597+
if(isFileReplaceOperation()) {
1598+
if (deleteStorageLocation != null) {
1599+
// Finalize the delete of the physical file
1600+
// (File service will double-check that the datafile no
1601+
// longer exists in the database, before proceeding to
1602+
// delete the physical file)
1603+
try {
1604+
fileService.finalizeFileDelete(deleteFileId, deleteStorageLocation);
1605+
} catch (IOException ioex) {
1606+
logger.warning("Failed to delete the physical file associated with the deleted datafile id="
1607+
+ deleteFileId + ", storage location: " + deleteStorageLocation);
16321608
}
16331609
}
16341610
}
1635-
1636-
msg("No matches found!");
1637-
addErrorSevere(getBundleErr("failed_to_remove_old_file_from_dataset"));
1638-
runMajorCleanup();
1639-
return false;
1611+
return true;
16401612
}
16411613

16421614

@@ -1711,13 +1683,6 @@ private boolean step_080_run_update_dataset_command_for_replace(){
17111683
return false;
17121684
}
17131685

1714-
// -----------------------------------------------------------
1715-
// Remove the "fileToReplace" from the current working version
1716-
// -----------------------------------------------------------
1717-
if (!step_085_auto_remove_filemetadata_to_replace_from_working_version()){
1718-
return false;
1719-
}
1720-
17211686
// -----------------------------------------------------------
17221687
// Set the "root file ids" and "previous file ids"
17231688
// THIS IS A KEY STEP - SPLIT IT OUT
@@ -1727,26 +1692,27 @@ private boolean step_080_run_update_dataset_command_for_replace(){
17271692
// -----------------------------------------------------------
17281693

17291694

1730-
/*
1731-
Check the root file id on fileToReplace, updating it if necessary
1732-
*/
1733-
if (fileToReplace.getRootDataFileId().equals(DataFile.ROOT_DATAFILE_ID_DEFAULT)){
1695+
if (fileToReplace.isReleased()) {
1696+
/*
1697+
* Check the root file id on fileToReplace, updating it if necessary
1698+
*/
1699+
if (fileToReplace.getRootDataFileId().equals(DataFile.ROOT_DATAFILE_ID_DEFAULT)) {
17341700

1735-
fileToReplace.setRootDataFileId(fileToReplace.getId());
1736-
fileToReplace = fileService.save(fileToReplace);
1737-
}
1738-
1739-
/*
1740-
Go through the final file list, settting the rootFileId and previousFileId
1741-
*/
1742-
for (DataFile df : finalFileList){
1743-
df.setPreviousDataFileId(fileToReplace.getId());
1744-
1745-
df.setRootDataFileId(fileToReplace.getRootDataFileId());
1746-
1747-
}
1701+
fileToReplace.setRootDataFileId(fileToReplace.getId());
1702+
fileToReplace = fileService.save(fileToReplace);
1703+
}
1704+
1705+
/*
1706+
* Go through the final file list, settting the rootFileId and previousFileId
1707+
*/
1708+
for (DataFile df : finalFileList) {
1709+
df.setPreviousDataFileId(fileToReplace.getId());
1710+
1711+
df.setRootDataFileId(fileToReplace.getRootDataFileId());
17481712

1749-
// Call the update dataset command
1713+
}
1714+
}
1715+
// Call the update dataset command which will delete the replaced filemetadata and file in needed (if file is not released)
17501716
//
17511717
return step_070_run_update_dataset_command();
17521718

src/main/java/edu/harvard/iq/dataverse/datasetutility/FileReplacePageHelper.java

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -111,13 +111,8 @@ public boolean handleNativeFileUpload(InputStream inputStream, String fullStorag
111111
}
112112

113113
OptionalFileParams ofp = null;
114+
ofp = new OptionalFileParams();
114115
if(checkSumValue != null) {
115-
try {
116-
ofp = new OptionalFileParams(null);
117-
} catch (DataFileTagException e) {
118-
// Shouldn't happen with null input
119-
e.printStackTrace();
120-
}
121116
ofp.setCheckSum(checkSumValue, checkSumType);
122117
}
123118
// Run 1st phase of replace

0 commit comments

Comments
 (0)