From 546f0fd34ac5674b41bf5af342c58528aa8e5a67 Mon Sep 17 00:00:00 2001 From: qqmyers Date: Fri, 11 Sep 2026 16:47:01 -0400 Subject: [PATCH 1/5] handle workflowcomments correctly, add datasetversionusers --- .../harvard/iq/dataverse/DatasetVersion.java | 14 ++++++- .../CuratePublishedDatasetVersionCommand.java | 37 ++++++++++++++----- .../harvard/iq/dataverse/api/DatasetsIT.java | 15 +++++++- 3 files changed, 54 insertions(+), 12 deletions(-) diff --git a/src/main/java/edu/harvard/iq/dataverse/DatasetVersion.java b/src/main/java/edu/harvard/iq/dataverse/DatasetVersion.java index 4bff7150b97..bf028b13675 100644 --- a/src/main/java/edu/harvard/iq/dataverse/DatasetVersion.java +++ b/src/main/java/edu/harvard/iq/dataverse/DatasetVersion.java @@ -183,11 +183,11 @@ public enum VersionState { private String jsonLd; @OneToMany(mappedBy="datasetVersion", cascade={CascadeType.REMOVE, CascadeType.MERGE, CascadeType.PERSIST}) - private List datasetVersionUsers; + private List datasetVersionUsers = new ArrayList<>(); // Is this the right mapping and cascading for when the workflowcomments table is being used for objects other than DatasetVersion? @OneToMany(mappedBy = "datasetVersion", cascade={CascadeType.REMOVE, CascadeType.MERGE, CascadeType.PERSIST}) - private List workflowComments; + private List workflowComments = new ArrayList<>(); /* * As of v6.7, the NULLS LAST part of the annotation below appears to not be working. Explicit sorting has been added in the getCurationStatuses() method. The annotation is kept since, if it does work @@ -2167,6 +2167,16 @@ public void removeCurationStatus(CurationStatus curationStatus) { curationStatus.setDatasetVersion(null); } + public void addWorkflowComment(WorkflowComment comment) { + comment.setDatasetVersion(this); + workflowComments.add(comment); + } + + public void addDatasetVersionUser(DatasetVersionUser dvu) { + dvu.setDatasetVersion(this); + datasetVersionUsers.add(dvu); + } + public CurationStatus getCurationStatusAsOfDate(Date date) { if (curationStatuses == null || curationStatuses.isEmpty()) { return null; diff --git a/src/main/java/edu/harvard/iq/dataverse/engine/command/impl/CuratePublishedDatasetVersionCommand.java b/src/main/java/edu/harvard/iq/dataverse/engine/command/impl/CuratePublishedDatasetVersionCommand.java index 1c57a9d4647..1e23750d72b 100644 --- a/src/main/java/edu/harvard/iq/dataverse/engine/command/impl/CuratePublishedDatasetVersionCommand.java +++ b/src/main/java/edu/harvard/iq/dataverse/engine/command/impl/CuratePublishedDatasetVersionCommand.java @@ -14,6 +14,7 @@ import edu.harvard.iq.dataverse.Dataset; import edu.harvard.iq.dataverse.DatasetField; import edu.harvard.iq.dataverse.DatasetVersion; +import edu.harvard.iq.dataverse.DatasetVersionUser; import edu.harvard.iq.dataverse.TermsOfUseAndAccess; import edu.harvard.iq.dataverse.CurationStatus; import edu.harvard.iq.dataverse.DataFile; @@ -91,12 +92,35 @@ public Dataset execute(CommandContext ctxt) throws CommandException { //Also set the fileaccessrequest boolean on the dataset to match the new terms getDataset().setFileAccessRequest(updateVersion.getTermsOfUseAndAccess().isFileAccessRequest()); + + // Transfer workflow comments from draft to published version List newComments = newVersion.getWorkflowComments(); - if (newComments!=null && newComments.size() >0) { - for(WorkflowComment wfc: newComments) { - wfc.setDatasetVersion(updateVersion); + if (newComments != null && newComments.size() > 0) { + for (WorkflowComment wfc : newComments) { + updateVersion.addWorkflowComment(wfc); } - updateVersion.getWorkflowComments().addAll(newComments); + newVersion.getWorkflowComments().clear(); + } + + // Transfer DatasetVersionUser entries from draft to published version + List draftDVUsers = newVersion.getDatasetVersionUsers(); + if (draftDVUsers != null && !draftDVUsers.isEmpty()) { + for (DatasetVersionUser dvu : draftDVUsers) { + boolean found = false; + for (DatasetVersionUser existingDvu : updateVersion.getDatasetVersionUsers()) { + if (existingDvu.getAuthenticatedUser().equals(dvu.getAuthenticatedUser())) { + if (dvu.getLastUpdateDate().after(existingDvu.getLastUpdateDate())) { + existingDvu.setLastUpdateDate(dvu.getLastUpdateDate()); + } + found = true; + break; + } + } + if (!found) { + updateVersion.addDatasetVersionUser(dvu); + } + } + newVersion.getDatasetVersionUsers().clear(); } // Transfer curation status entries from draft to published version @@ -232,11 +256,6 @@ public Dataset execute(CommandContext ctxt) throws CommandException { updateDatasetUser(ctxt); - // ToDo - see if there are other DatasetVersionUser entries unique to the draft - // version that should be moved to the last published version - // As this command is intended for minor fixes, often done by the person pushing - // the update-current-version button, this is probably a minor issue. - return savedDataset; } diff --git a/src/test/java/edu/harvard/iq/dataverse/api/DatasetsIT.java b/src/test/java/edu/harvard/iq/dataverse/api/DatasetsIT.java index 1e1aad484e4..2e76a82823f 100644 --- a/src/test/java/edu/harvard/iq/dataverse/api/DatasetsIT.java +++ b/src/test/java/edu/harvard/iq/dataverse/api/DatasetsIT.java @@ -4374,7 +4374,20 @@ public void testCuratePublishedDatasetVersionCommand() throws IOException { updateTitle.prettyPrint(); updateTitle.then().assertThat() .statusCode(OK.getStatusCode()); - + + // Add a workflow comment to the draft + UtilIT.submitDatasetForReview(datasetPid, apiToken).then().assertThat().statusCode(OK.getStatusCode()); + JsonObjectBuilder curatorComment = JsonUtil.createObjectBuilder(); + curatorComment.add("reasonForReturn", "Please fix the metadata."); + UtilIT.returnDatasetToAuthor(datasetPid, curatorComment.build(), apiToken).then().assertThat().statusCode(OK.getStatusCode()); + + // Add another DatasetVersionUser by having another superuser edit the draft + Response createUser2 = UtilIT.createRandomUser(); + String apiToken2 = UtilIT.getApiTokenFromResponse(createUser2); + String username2 = UtilIT.getUsernameFromResponse(createUser2); + UtilIT.setSuperuserStatus(username2, true).then().assertThat().statusCode(OK.getStatusCode()); + UtilIT.updateDatasetMetadataViaNative(datasetPid, pathToJsonFile, apiToken2).then().assertThat().statusCode(OK.getStatusCode()); + // shouldn't be able to update current unless you're a super user UtilIT.publishDatasetViaNativeApi(datasetId, "updatecurrent", apiToken).then().assertThat().statusCode(FORBIDDEN.getStatusCode()); From d8d69ffe4081ebb5e0fd48efcc39aba0dcfcd219 Mon Sep 17 00:00:00 2001 From: qqmyers Date: Mon, 14 Sep 2026 12:33:46 -0400 Subject: [PATCH 2/5] test update, release note --- doc/release-notes/12699-CurateCommand updates.md | 3 +++ .../java/edu/harvard/iq/dataverse/api/DatasetsIT.java | 8 ++++++++ src/test/java/edu/harvard/iq/dataverse/api/UtilIT.java | 7 +++++++ 3 files changed, 18 insertions(+) create mode 100644 doc/release-notes/12699-CurateCommand updates.md diff --git a/doc/release-notes/12699-CurateCommand updates.md b/doc/release-notes/12699-CurateCommand updates.md new file mode 100644 index 00000000000..f3f5eb23718 --- /dev/null +++ b/doc/release-notes/12699-CurateCommand updates.md @@ -0,0 +1,3 @@ +## Updates to the "Update Current Version" Publishing Option +This release fixes a bug in the Update Current Version functionality that would cause it to fail if the submit for review/ return to author functionality had been used with the dataset. +It also assures that people who contributed to the draft are added to the list of people who contributed to the published version being updated (which was not done previously). diff --git a/src/test/java/edu/harvard/iq/dataverse/api/DatasetsIT.java b/src/test/java/edu/harvard/iq/dataverse/api/DatasetsIT.java index 2e76a82823f..22348a75556 100644 --- a/src/test/java/edu/harvard/iq/dataverse/api/DatasetsIT.java +++ b/src/test/java/edu/harvard/iq/dataverse/api/DatasetsIT.java @@ -4404,6 +4404,14 @@ public void testCuratePublishedDatasetVersionCommand() throws IOException { getDatasetJsonAfterUpdate.then().assertThat() .statusCode(OK.getStatusCode()); + // Check that the DatasetVersionUser was transferred by checking the contributors list in the version summary + Response getCompareSummary = UtilIT.getCompareSummary(datasetId, apiToken); + getCompareSummary.prettyPrint(); + getCompareSummary.then().assertThat() + .statusCode(OK.getStatusCode()) + .body("data[0].contributors", org.hamcrest.Matchers.containsString(username)) + .body("data[0].contributors", org.hamcrest.Matchers.containsString(username2)); + //Check that the draft version is gone Response getDraft1 = UtilIT.getDatasetVersion(datasetPid, DS_VERSION_DRAFT, apiToken); getDraft1.then().assertThat() diff --git a/src/test/java/edu/harvard/iq/dataverse/api/UtilIT.java b/src/test/java/edu/harvard/iq/dataverse/api/UtilIT.java index 1c1d7116dc3..83f25fb9c26 100644 --- a/src/test/java/edu/harvard/iq/dataverse/api/UtilIT.java +++ b/src/test/java/edu/harvard/iq/dataverse/api/UtilIT.java @@ -2414,6 +2414,13 @@ static Response nativeGet(Integer datasetId, String apiToken) { return response; } + static Response getCompareSummary(Integer datasetId, String apiToken) { + Response response = given() + .header(API_TOKEN_HTTP_HEADER, apiToken) + .get("/api/datasets/" + datasetId + "/versions/compareSummary"); + return response; + } + static Response privateUrlGet(Integer datasetId, String apiToken) { Response response = given() .header(API_TOKEN_HTTP_HEADER, apiToken) From 63407ece4b91651c94b54079b15324a99d4a0ac2 Mon Sep 17 00:00:00 2001 From: qqmyers Date: Thu, 17 Sep 2026 10:58:50 -0400 Subject: [PATCH 3/5] fix issue w/ found users --- .../impl/CuratePublishedDatasetVersionCommand.java | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/src/main/java/edu/harvard/iq/dataverse/engine/command/impl/CuratePublishedDatasetVersionCommand.java b/src/main/java/edu/harvard/iq/dataverse/engine/command/impl/CuratePublishedDatasetVersionCommand.java index 1e23750d72b..73d6ee6206f 100644 --- a/src/main/java/edu/harvard/iq/dataverse/engine/command/impl/CuratePublishedDatasetVersionCommand.java +++ b/src/main/java/edu/harvard/iq/dataverse/engine/command/impl/CuratePublishedDatasetVersionCommand.java @@ -103,9 +103,9 @@ public Dataset execute(CommandContext ctxt) throws CommandException { } // Transfer DatasetVersionUser entries from draft to published version - List draftDVUsers = newVersion.getDatasetVersionUsers(); - if (draftDVUsers != null && !draftDVUsers.isEmpty()) { - for (DatasetVersionUser dvu : draftDVUsers) { + if (newVersion.getDatasetVersionUsers() != null && !newVersion.getDatasetVersionUsers().isEmpty()) { + for (Iterator it = newVersion.getDatasetVersionUsers().iterator(); it.hasNext();) { + DatasetVersionUser dvu = it.next(); boolean found = false; for (DatasetVersionUser existingDvu : updateVersion.getDatasetVersionUsers()) { if (existingDvu.getAuthenticatedUser().equals(dvu.getAuthenticatedUser())) { @@ -116,11 +116,14 @@ public Dataset execute(CommandContext ctxt) throws CommandException { break; } } - if (!found) { + if (found) { + it.remove(); + ctxt.em().remove(dvu); + } else { updateVersion.addDatasetVersionUser(dvu); + it.remove(); } } - newVersion.getDatasetVersionUsers().clear(); } // Transfer curation status entries from draft to published version From d8ba51944b35de032d1325c2f48ab0f0f4f14c35 Mon Sep 17 00:00:00 2001 From: qqmyers Date: Thu, 17 Sep 2026 12:05:46 -0400 Subject: [PATCH 4/5] test update to add found dvu case --- .../harvard/iq/dataverse/api/DatasetsIT.java | 42 +++++++++++++------ 1 file changed, 29 insertions(+), 13 deletions(-) diff --git a/src/test/java/edu/harvard/iq/dataverse/api/DatasetsIT.java b/src/test/java/edu/harvard/iq/dataverse/api/DatasetsIT.java index 22348a75556..441b9301dbd 100644 --- a/src/test/java/edu/harvard/iq/dataverse/api/DatasetsIT.java +++ b/src/test/java/edu/harvard/iq/dataverse/api/DatasetsIT.java @@ -4246,6 +4246,15 @@ public void testUpdateDatasetTermsOfAccess() throws IOException { + private Response updateDatasetMetadataUnique(String datasetPid, String pathToJsonFile, String title, String apiToken) { + String json = UtilIT.getDatasetJson(pathToJsonFile).replace("newTitle", title); + return given() + .header(API_TOKEN_HTTP_HEADER, apiToken) + .body(json) + .contentType("application/json") + .put("/api/datasets/:persistentId/versions/" + DS_VERSION_DRAFT + "?persistentId=" + datasetPid); + } + @Test public void testCuratePublishedDatasetVersionCommand() throws IOException { Response createUser = UtilIT.createRandomUser(); @@ -4255,6 +4264,11 @@ public void testCuratePublishedDatasetVersionCommand() throws IOException { String apiToken = UtilIT.getApiTokenFromResponse(createUser); String username = UtilIT.getUsernameFromResponse(createUser); + // Create another user to test the "found" case during curation + Response createUser2 = UtilIT.createRandomUser(); + String apiToken2 = UtilIT.getApiTokenFromResponse(createUser2); + String username2 = UtilIT.getUsernameFromResponse(createUser2); + String pathToJsonFile = "doc/sphinx-guides/source/_static/api/dataset-update-metadata.json"; Response createDataverse = UtilIT.createRandomDataverse(apiToken); createDataverse.prettyPrint(); @@ -4358,7 +4372,8 @@ public void testCuratePublishedDatasetVersionCommand() throws IOException { editDDIResponse.prettyPrint(); assertEquals(200, editDDIResponse.getStatusCode()); - + // Add username2 to the draft before it is published as major version + updateDatasetMetadataUnique(datasetPid, pathToJsonFile, "Title Published", apiToken2).then().assertThat().statusCode(OK.getStatusCode()); UtilIT.publishDataverseViaNativeApi(dataverseAlias, apiToken).then().assertThat().statusCode(OK.getStatusCode()); UtilIT.publishDatasetViaNativeApi(datasetId, "major", apiToken).then().assertThat().statusCode(OK.getStatusCode()); @@ -4369,12 +4384,8 @@ public void testCuratePublishedDatasetVersionCommand() throws IOException { .statusCode(OK.getStatusCode()) .body("data.latestVersion.files[0].label", equalTo("dct.tab")); - String pathToJsonFile = "doc/sphinx-guides/source/_static/api/dataset-update-metadata.json"; - Response updateTitle = UtilIT.updateDatasetMetadataViaNative(datasetPid, pathToJsonFile, apiToken); - updateTitle.prettyPrint(); - updateTitle.then().assertThat() - .statusCode(OK.getStatusCode()); - + updateDatasetMetadataUnique(datasetPid, pathToJsonFile, "Title Draft 1", apiToken).then().assertThat().statusCode(OK.getStatusCode()); + // Add a workflow comment to the draft UtilIT.submitDatasetForReview(datasetPid, apiToken).then().assertThat().statusCode(OK.getStatusCode()); JsonObjectBuilder curatorComment = JsonUtil.createObjectBuilder(); @@ -4382,11 +4393,15 @@ public void testCuratePublishedDatasetVersionCommand() throws IOException { UtilIT.returnDatasetToAuthor(datasetPid, curatorComment.build(), apiToken).then().assertThat().statusCode(OK.getStatusCode()); // Add another DatasetVersionUser by having another superuser edit the draft - Response createUser2 = UtilIT.createRandomUser(); - String apiToken2 = UtilIT.getApiTokenFromResponse(createUser2); - String username2 = UtilIT.getUsernameFromResponse(createUser2); + // username2 is already in the published version (found = true), username3 is new (found = false) + Response createUser3 = UtilIT.createRandomUser(); + String apiToken3 = UtilIT.getApiTokenFromResponse(createUser3); + String username3 = UtilIT.getUsernameFromResponse(createUser3); + UtilIT.setSuperuserStatus(username3, true).then().assertThat().statusCode(OK.getStatusCode()); UtilIT.setSuperuserStatus(username2, true).then().assertThat().statusCode(OK.getStatusCode()); - UtilIT.updateDatasetMetadataViaNative(datasetPid, pathToJsonFile, apiToken2).then().assertThat().statusCode(OK.getStatusCode()); + + updateDatasetMetadataUnique(datasetPid, pathToJsonFile, "Title Draft 2", apiToken2).then().assertThat().statusCode(OK.getStatusCode()); + updateDatasetMetadataUnique(datasetPid, pathToJsonFile, "Title Draft 3", apiToken3).then().assertThat().statusCode(OK.getStatusCode()); // shouldn't be able to update current unless you're a super user @@ -4404,13 +4419,14 @@ public void testCuratePublishedDatasetVersionCommand() throws IOException { getDatasetJsonAfterUpdate.then().assertThat() .statusCode(OK.getStatusCode()); - // Check that the DatasetVersionUser was transferred by checking the contributors list in the version summary + // Check that the DatasetVersionUsers were transferred/updated by checking the contributors list Response getCompareSummary = UtilIT.getCompareSummary(datasetId, apiToken); getCompareSummary.prettyPrint(); getCompareSummary.then().assertThat() .statusCode(OK.getStatusCode()) .body("data[0].contributors", org.hamcrest.Matchers.containsString(username)) - .body("data[0].contributors", org.hamcrest.Matchers.containsString(username2)); + .body("data[0].contributors", org.hamcrest.Matchers.containsString(username2)) + .body("data[0].contributors", org.hamcrest.Matchers.containsString(username3)); //Check that the draft version is gone Response getDraft1 = UtilIT.getDatasetVersion(datasetPid, DS_VERSION_DRAFT, apiToken); From ac6612488441023b63e6ca2a133315cfcb53c78d Mon Sep 17 00:00:00 2001 From: qqmyers Date: Thu, 17 Sep 2026 13:46:47 -0400 Subject: [PATCH 5/5] fix test 401 issue --- src/test/java/edu/harvard/iq/dataverse/api/DatasetsIT.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/test/java/edu/harvard/iq/dataverse/api/DatasetsIT.java b/src/test/java/edu/harvard/iq/dataverse/api/DatasetsIT.java index 441b9301dbd..3a3bf390506 100644 --- a/src/test/java/edu/harvard/iq/dataverse/api/DatasetsIT.java +++ b/src/test/java/edu/harvard/iq/dataverse/api/DatasetsIT.java @@ -4268,6 +4268,7 @@ public void testCuratePublishedDatasetVersionCommand() throws IOException { Response createUser2 = UtilIT.createRandomUser(); String apiToken2 = UtilIT.getApiTokenFromResponse(createUser2); String username2 = UtilIT.getUsernameFromResponse(createUser2); + UtilIT.setSuperuserStatus(username2, true).then().assertThat().statusCode(OK.getStatusCode()); String pathToJsonFile = "doc/sphinx-guides/source/_static/api/dataset-update-metadata.json"; Response createDataverse = UtilIT.createRandomDataverse(apiToken); @@ -4398,7 +4399,6 @@ public void testCuratePublishedDatasetVersionCommand() throws IOException { String apiToken3 = UtilIT.getApiTokenFromResponse(createUser3); String username3 = UtilIT.getUsernameFromResponse(createUser3); UtilIT.setSuperuserStatus(username3, true).then().assertThat().statusCode(OK.getStatusCode()); - UtilIT.setSuperuserStatus(username2, true).then().assertThat().statusCode(OK.getStatusCode()); updateDatasetMetadataUnique(datasetPid, pathToJsonFile, "Title Draft 2", apiToken2).then().assertThat().statusCode(OK.getStatusCode()); updateDatasetMetadataUnique(datasetPid, pathToJsonFile, "Title Draft 3", apiToken3).then().assertThat().statusCode(OK.getStatusCode());