From f902a3ec75a6ca1d23d81f02585902b5873c1fbd Mon Sep 17 00:00:00 2001 From: Philip Durbin Date: Thu, 18 Jan 2024 15:55:32 -0500 Subject: [PATCH 01/13] add API endpoint to return file citation #10240 --- .../edu/harvard/iq/dataverse/api/Files.java | 18 +++++++++ .../edu/harvard/iq/dataverse/api/FilesIT.java | 40 +++++++++++++++++++ .../edu/harvard/iq/dataverse/api/UtilIT.java | 7 ++++ 3 files changed, 65 insertions(+) diff --git a/src/main/java/edu/harvard/iq/dataverse/api/Files.java b/src/main/java/edu/harvard/iq/dataverse/api/Files.java index 5d400ee1438..f4282b794b1 100644 --- a/src/main/java/edu/harvard/iq/dataverse/api/Files.java +++ b/src/main/java/edu/harvard/iq/dataverse/api/Files.java @@ -2,6 +2,7 @@ import com.google.gson.Gson; import com.google.gson.JsonObject; +import edu.harvard.iq.dataverse.DataCitation; import edu.harvard.iq.dataverse.DataFile; import edu.harvard.iq.dataverse.DataFileServiceBean; import edu.harvard.iq.dataverse.DataFileTag; @@ -931,4 +932,21 @@ public Response getHasBeenDeleted(@Context ContainerRequestContext crc, @PathPar return ok(dataFileServiceBean.hasBeenDeleted(dataFile)); }, getRequestUser(crc)); } + + @GET + @AuthRequired + @Path("{id}/citation") + public Response getFileCitation(@Context ContainerRequestContext crc, @PathParam("id") String fileIdOrPersistentId) { + try { + DataverseRequest req = createDataverseRequest(getRequestUser(crc)); + final DataFile df = execCommand(new GetDataFileCommand(req, findDataFileOrDie(fileIdOrPersistentId))); + FileMetadata fm = df.getLatestFileMetadata(); + boolean direct = false; + DataCitation citation = new DataCitation(fm, direct); + return ok(citation.toString(true)); + } catch (WrappedResponse ex) { + return ex.getResponse(); + } + } + } diff --git a/src/test/java/edu/harvard/iq/dataverse/api/FilesIT.java b/src/test/java/edu/harvard/iq/dataverse/api/FilesIT.java index 915f82a6de2..853d92aac0e 100644 --- a/src/test/java/edu/harvard/iq/dataverse/api/FilesIT.java +++ b/src/test/java/edu/harvard/iq/dataverse/api/FilesIT.java @@ -33,6 +33,7 @@ import jakarta.json.JsonObjectBuilder; import static jakarta.ws.rs.core.Response.Status.*; +import java.time.Year; import org.hamcrest.CoreMatchers; import org.hamcrest.Matchers; import org.junit.jupiter.api.AfterAll; @@ -2483,4 +2484,43 @@ public void testCollectionStorageQuotas() { UtilIT.deleteSetting(SettingsServiceBean.Key.UseStorageQuotas); } + + @Test + public void getFileCitation() throws IOException { + Response createUser = UtilIT.createRandomUser(); + createUser.then().assertThat().statusCode(OK.getStatusCode()); + String apiToken = UtilIT.getApiTokenFromResponse(createUser); + + Response createDataverseResponse = UtilIT.createRandomDataverse(apiToken); + createDataverseResponse.then().assertThat().statusCode(CREATED.getStatusCode()); + String dataverseAlias = UtilIT.getAliasFromResponse(createDataverseResponse); + + Response createDatasetResponse = UtilIT.createRandomDatasetViaNativeApi(dataverseAlias, apiToken); + createDatasetResponse.prettyPrint(); + createDatasetResponse.then().assertThat().statusCode(CREATED.getStatusCode()); + Integer datasetId = JsonPath.from(createDatasetResponse.body().asString()).getInt("data.id"); + String datasetPid = JsonPath.from(createDatasetResponse.body().asString()).getString("data.persistentId"); + + Path pathToTxt = Paths.get(java.nio.file.Files.createTempDirectory(null) + File.separator + "file.txt"); + String contentOfTxt = "foobar"; + java.nio.file.Files.write(pathToTxt, contentOfTxt.getBytes()); + + Response uploadFileTxt = UtilIT.uploadFileViaNative(datasetId.toString(), pathToTxt.toString(), apiToken); + uploadFileTxt.prettyPrint(); + uploadFileTxt.then().assertThat() + .statusCode(OK.getStatusCode()) + .body("data.files[0].label", equalTo("file.txt")); + + Integer fileId = JsonPath.from(uploadFileTxt.body().asString()).getInt("data.files[0].dataFile.id"); + + String pidAsUrl = "https://doi.org/" + datasetPid.split("doi:")[1]; + int currentYear = Year.now().getValue(); + + Response getFileCitation = UtilIT.getFileCitation(fileId, true, apiToken); + getFileCitation.prettyPrint(); + getFileCitation.then().assertThat() + .statusCode(OK.getStatusCode()) + .body("data.message", equalTo("Finch, Fiona, " + currentYear + ", \"Darwin's Finches\", " + pidAsUrl + ", Root, DRAFT VERSION; file.txt [fileName]")); + } + } 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 6af3f8a0a09..9b9f8ddff47 100644 --- a/src/test/java/edu/harvard/iq/dataverse/api/UtilIT.java +++ b/src/test/java/edu/harvard/iq/dataverse/api/UtilIT.java @@ -3459,6 +3459,13 @@ static Response getDatasetVersionCitation(Integer datasetId, String version, boo return response; } + static Response getFileCitation(Integer fileId, boolean getDraft, String apiToken) { + Response response = given() + .header(API_TOKEN_HTTP_HEADER, apiToken) + .get("/api/files/" + fileId + "/citation"); + return response; + } + static Response getVersionFiles(Integer datasetId, String version, Integer limit, From 85018f5182fbdd8f59dad75e9e9612ac7c657c54 Mon Sep 17 00:00:00 2001 From: Philip Durbin Date: Mon, 22 Jan 2024 10:42:28 -0500 Subject: [PATCH 02/13] make assertings on draft vs published #10240 --- .../edu/harvard/iq/dataverse/api/FilesIT.java | 23 +++++++++++++++++++ .../edu/harvard/iq/dataverse/api/UtilIT.java | 9 ++++---- 2 files changed, 28 insertions(+), 4 deletions(-) diff --git a/src/test/java/edu/harvard/iq/dataverse/api/FilesIT.java b/src/test/java/edu/harvard/iq/dataverse/api/FilesIT.java index 853d92aac0e..49e6c5c4f22 100644 --- a/src/test/java/edu/harvard/iq/dataverse/api/FilesIT.java +++ b/src/test/java/edu/harvard/iq/dataverse/api/FilesIT.java @@ -2516,11 +2516,34 @@ public void getFileCitation() throws IOException { String pidAsUrl = "https://doi.org/" + datasetPid.split("doi:")[1]; int currentYear = Year.now().getValue(); + Response draftUnauthNoApitoken = UtilIT.getFileCitation(fileId, true, null); + draftUnauthNoApitoken.then().assertThat().statusCode(UNAUTHORIZED.getStatusCode()); + + Response createNoPermsUser = UtilIT.createRandomUser(); + createNoPermsUser.then().assertThat().statusCode(OK.getStatusCode()); + String noPermsApiToken = UtilIT.getApiTokenFromResponse(createNoPermsUser); + + Response draftUnauthNoPermsApiToken = UtilIT.getFileCitation(fileId, true, noPermsApiToken); + draftUnauthNoPermsApiToken.then().assertThat().statusCode(UNAUTHORIZED.getStatusCode()); + Response getFileCitation = UtilIT.getFileCitation(fileId, true, apiToken); getFileCitation.prettyPrint(); getFileCitation.then().assertThat() .statusCode(OK.getStatusCode()) .body("data.message", equalTo("Finch, Fiona, " + currentYear + ", \"Darwin's Finches\", " + pidAsUrl + ", Root, DRAFT VERSION; file.txt [fileName]")); + + Response publishDataverseResponse = UtilIT.publishDataverseViaNativeApi(dataverseAlias, apiToken); + publishDataverseResponse.then().assertThat().statusCode(OK.getStatusCode()); + + Response publishDatasetResponse = UtilIT.publishDatasetViaNativeApi(datasetId, "major", apiToken); + publishDatasetResponse.then().assertThat().statusCode(OK.getStatusCode()); + + Response publishedNoApiTokenNeeded = UtilIT.getFileCitation(fileId, true, null); + publishedNoApiTokenNeeded.then().assertThat().statusCode(OK.getStatusCode()); + + Response publishedNoPermsApiTokenAllowed = UtilIT.getFileCitation(fileId, true, noPermsApiToken); + publishedNoPermsApiTokenAllowed.then().assertThat().statusCode(OK.getStatusCode()); + } } 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 9b9f8ddff47..946bc6d5c83 100644 --- a/src/test/java/edu/harvard/iq/dataverse/api/UtilIT.java +++ b/src/test/java/edu/harvard/iq/dataverse/api/UtilIT.java @@ -3460,10 +3460,11 @@ static Response getDatasetVersionCitation(Integer datasetId, String version, boo } static Response getFileCitation(Integer fileId, boolean getDraft, String apiToken) { - Response response = given() - .header(API_TOKEN_HTTP_HEADER, apiToken) - .get("/api/files/" + fileId + "/citation"); - return response; + var spec = given(); + if (apiToken != null) { + spec.header(API_TOKEN_HTTP_HEADER, apiToken); + } + return spec.get("/api/files/" + fileId + "/citation"); } static Response getVersionFiles(Integer datasetId, From f34f82be7281e05cf80cee461ed948187f0a537b Mon Sep 17 00:00:00 2001 From: Philip Durbin Date: Mon, 22 Jan 2024 16:20:43 -0500 Subject: [PATCH 03/13] handle versions for "get data file citation" API #10240 --- .../edu/harvard/iq/dataverse/api/Files.java | 48 +++++++++++++++++-- .../edu/harvard/iq/dataverse/api/FilesIT.java | 45 ++++++++++++++--- .../edu/harvard/iq/dataverse/api/UtilIT.java | 4 +- 3 files changed, 85 insertions(+), 12 deletions(-) diff --git a/src/main/java/edu/harvard/iq/dataverse/api/Files.java b/src/main/java/edu/harvard/iq/dataverse/api/Files.java index f4282b794b1..c30503199e0 100644 --- a/src/main/java/edu/harvard/iq/dataverse/api/Files.java +++ b/src/main/java/edu/harvard/iq/dataverse/api/Files.java @@ -19,6 +19,7 @@ import edu.harvard.iq.dataverse.GuestbookResponseServiceBean; import edu.harvard.iq.dataverse.TermsOfUseAndAccessValidator; import edu.harvard.iq.dataverse.UserNotificationServiceBean; +import static edu.harvard.iq.dataverse.api.Datasets.handleVersion; import edu.harvard.iq.dataverse.api.auth.AuthRequired; import edu.harvard.iq.dataverse.authorization.Permission; import edu.harvard.iq.dataverse.authorization.users.ApiToken; @@ -28,11 +29,16 @@ import edu.harvard.iq.dataverse.datasetutility.DataFileTagException; import edu.harvard.iq.dataverse.datasetutility.NoFilesException; import edu.harvard.iq.dataverse.datasetutility.OptionalFileParams; +import edu.harvard.iq.dataverse.engine.command.Command; import edu.harvard.iq.dataverse.engine.command.DataverseRequest; import edu.harvard.iq.dataverse.engine.command.exception.CommandException; import edu.harvard.iq.dataverse.engine.command.exception.IllegalCommandException; import edu.harvard.iq.dataverse.engine.command.impl.GetDataFileCommand; +import edu.harvard.iq.dataverse.engine.command.impl.GetDraftDatasetVersionCommand; import edu.harvard.iq.dataverse.engine.command.impl.GetDraftFileMetadataIfAvailableCommand; +import edu.harvard.iq.dataverse.engine.command.impl.GetLatestAccessibleDatasetVersionCommand; +import edu.harvard.iq.dataverse.engine.command.impl.GetLatestPublishedDatasetVersionCommand; +import edu.harvard.iq.dataverse.engine.command.impl.GetSpecificPublishedDatasetVersionCommand; import edu.harvard.iq.dataverse.engine.command.impl.RedetectFileTypeCommand; import edu.harvard.iq.dataverse.engine.command.impl.RestrictFileCommand; import edu.harvard.iq.dataverse.engine.command.impl.UningestFileCommand; @@ -933,14 +939,50 @@ public Response getHasBeenDeleted(@Context ContainerRequestContext crc, @PathPar }, getRequestUser(crc)); } + /** + * @param fileIdOrPersistentId Database ID or PID of the data file. + * @param dsVersionString The version of the dataset, such as 1.0, :draft, + * :latest-published, etc. + */ @GET @AuthRequired - @Path("{id}/citation") - public Response getFileCitation(@Context ContainerRequestContext crc, @PathParam("id") String fileIdOrPersistentId) { + @Path("{id}/versions/{dsVersionString}/citation") + public Response getFileCitationByVersion(@Context ContainerRequestContext crc, @PathParam("id") String fileIdOrPersistentId, @PathParam("dsVersionString") String dsVersionString) { try { DataverseRequest req = createDataverseRequest(getRequestUser(crc)); final DataFile df = execCommand(new GetDataFileCommand(req, findDataFileOrDie(fileIdOrPersistentId))); - FileMetadata fm = df.getLatestFileMetadata(); + Dataset ds = df.getOwner(); + // Adapted from getDatasetVersionOrDie + // includeDeaccessioned and checkPermsWhenDeaccessioned were removed + // because they aren't needed. + DatasetVersion dsv = execCommand(handleVersion(dsVersionString, new Datasets.DsVersionHandler>() { + + @Override + public Command handleLatest() { + return new GetLatestAccessibleDatasetVersionCommand(req, ds); + } + + @Override + public Command handleDraft() { + return new GetDraftDatasetVersionCommand(req, ds); + } + + @Override + public Command handleSpecific(long major, long minor) { + return new GetSpecificPublishedDatasetVersionCommand(req, ds, major, minor); + } + + @Override + public Command handleLatestPublished() { + return new GetLatestPublishedDatasetVersionCommand(req, ds); + } + })); + + Long getDatasetVersionID = dsv.getId(); + FileMetadata fm = dataFileServiceBean.findFileMetadataByDatasetVersionIdAndDataFileId(getDatasetVersionID, df.getId()); + if (fm == null) { + return notFound("File could not be found."); + } boolean direct = false; DataCitation citation = new DataCitation(fm, direct); return ok(citation.toString(true)); diff --git a/src/test/java/edu/harvard/iq/dataverse/api/FilesIT.java b/src/test/java/edu/harvard/iq/dataverse/api/FilesIT.java index 49e6c5c4f22..4bc7456e7e7 100644 --- a/src/test/java/edu/harvard/iq/dataverse/api/FilesIT.java +++ b/src/test/java/edu/harvard/iq/dataverse/api/FilesIT.java @@ -17,6 +17,7 @@ import edu.harvard.iq.dataverse.settings.SettingsServiceBean; import edu.harvard.iq.dataverse.util.BundleUtil; import edu.harvard.iq.dataverse.util.SystemConfig; +import edu.harvard.iq.dataverse.util.json.JsonUtil; import java.io.File; import java.io.IOException; @@ -2501,6 +2502,13 @@ public void getFileCitation() throws IOException { Integer datasetId = JsonPath.from(createDatasetResponse.body().asString()).getInt("data.id"); String datasetPid = JsonPath.from(createDatasetResponse.body().asString()).getString("data.persistentId"); + Response getDatasetVersionCitationResponse = UtilIT.getDatasetVersionCitation(datasetId, DS_VERSION_DRAFT, false, apiToken); + getDatasetVersionCitationResponse.prettyPrint(); + getDatasetVersionCitationResponse.then().assertThat() + .statusCode(OK.getStatusCode()) + // We check that the returned message contains information expected for the citation string + .body("data.message", containsString("DRAFT VERSION")); + Path pathToTxt = Paths.get(java.nio.file.Files.createTempDirectory(null) + File.separator + "file.txt"); String contentOfTxt = "foobar"; java.nio.file.Files.write(pathToTxt, contentOfTxt.getBytes()); @@ -2516,19 +2524,21 @@ public void getFileCitation() throws IOException { String pidAsUrl = "https://doi.org/" + datasetPid.split("doi:")[1]; int currentYear = Year.now().getValue(); - Response draftUnauthNoApitoken = UtilIT.getFileCitation(fileId, true, null); + Response draftUnauthNoApitoken = UtilIT.getFileCitation(fileId, DS_VERSION_DRAFT, null); + draftUnauthNoApitoken.prettyPrint(); draftUnauthNoApitoken.then().assertThat().statusCode(UNAUTHORIZED.getStatusCode()); Response createNoPermsUser = UtilIT.createRandomUser(); createNoPermsUser.then().assertThat().statusCode(OK.getStatusCode()); String noPermsApiToken = UtilIT.getApiTokenFromResponse(createNoPermsUser); - Response draftUnauthNoPermsApiToken = UtilIT.getFileCitation(fileId, true, noPermsApiToken); + Response draftUnauthNoPermsApiToken = UtilIT.getFileCitation(fileId, DS_VERSION_DRAFT, noPermsApiToken); + draftUnauthNoPermsApiToken.prettyPrint(); draftUnauthNoPermsApiToken.then().assertThat().statusCode(UNAUTHORIZED.getStatusCode()); - Response getFileCitation = UtilIT.getFileCitation(fileId, true, apiToken); - getFileCitation.prettyPrint(); - getFileCitation.then().assertThat() + Response getFileCitationDraft = UtilIT.getFileCitation(fileId, DS_VERSION_DRAFT, apiToken); + getFileCitationDraft.prettyPrint(); + getFileCitationDraft.then().assertThat() .statusCode(OK.getStatusCode()) .body("data.message", equalTo("Finch, Fiona, " + currentYear + ", \"Darwin's Finches\", " + pidAsUrl + ", Root, DRAFT VERSION; file.txt [fileName]")); @@ -2538,12 +2548,33 @@ public void getFileCitation() throws IOException { Response publishDatasetResponse = UtilIT.publishDatasetViaNativeApi(datasetId, "major", apiToken); publishDatasetResponse.then().assertThat().statusCode(OK.getStatusCode()); - Response publishedNoApiTokenNeeded = UtilIT.getFileCitation(fileId, true, null); + Response publishedNoApiTokenNeeded = UtilIT.getFileCitation(fileId, "1.0", null); publishedNoApiTokenNeeded.then().assertThat().statusCode(OK.getStatusCode()); - Response publishedNoPermsApiTokenAllowed = UtilIT.getFileCitation(fileId, true, noPermsApiToken); + Response publishedNoPermsApiTokenAllowed = UtilIT.getFileCitation(fileId, "1.0", noPermsApiToken); publishedNoPermsApiTokenAllowed.then().assertThat().statusCode(OK.getStatusCode()); + String updateJsonString = """ +{ + "label": "foo.txt" +} +"""; + + Response updateMetadataResponse = UtilIT.updateFileMetadata(fileId.toString(), updateJsonString, apiToken); + updateMetadataResponse.prettyPrint(); + assertEquals(OK.getStatusCode(), updateMetadataResponse.getStatusCode()); + + Response getFileCitationPostV1Draft = UtilIT.getFileCitation(fileId, DS_VERSION_DRAFT, apiToken); + getFileCitationPostV1Draft.prettyPrint(); + getFileCitationPostV1Draft.then().assertThat() + .statusCode(OK.getStatusCode()) + .body("data.message", equalTo("Finch, Fiona, " + currentYear + ", \"Darwin's Finches\", " + pidAsUrl + ", Root, DRAFT VERSION; foo.txt [fileName]")); + + Response getFileCitationV1Filename = UtilIT.getFileCitation(fileId, "1.0", apiToken); + getFileCitationV1Filename.prettyPrint(); + getFileCitationV1Filename.then().assertThat() + .statusCode(OK.getStatusCode()) + .body("data.message", equalTo("Finch, Fiona, " + currentYear + ", \"Darwin's Finches\", " + pidAsUrl + ", Root, V1; file.txt [fileName]")); } } 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 946bc6d5c83..520d68428a3 100644 --- a/src/test/java/edu/harvard/iq/dataverse/api/UtilIT.java +++ b/src/test/java/edu/harvard/iq/dataverse/api/UtilIT.java @@ -3459,12 +3459,12 @@ static Response getDatasetVersionCitation(Integer datasetId, String version, boo return response; } - static Response getFileCitation(Integer fileId, boolean getDraft, String apiToken) { + static Response getFileCitation(Integer fileId, String datasetVersion, String apiToken) { var spec = given(); if (apiToken != null) { spec.header(API_TOKEN_HTTP_HEADER, apiToken); } - return spec.get("/api/files/" + fileId + "/citation"); + return spec.get("/api/files/" + fileId + "/versions/" + datasetVersion + "/citation"); } static Response getVersionFiles(Integer datasetId, From c999ac7721e4202a36790c23ab8acadf95b6ba8c Mon Sep 17 00:00:00 2001 From: Philip Durbin Date: Tue, 23 Jan 2024 11:28:04 -0500 Subject: [PATCH 04/13] handle deaccessioned versions #10240 --- .../edu/harvard/iq/dataverse/api/Files.java | 11 ++++-- .../edu/harvard/iq/dataverse/api/FilesIT.java | 34 ++++++++++++++++--- 2 files changed, 38 insertions(+), 7 deletions(-) diff --git a/src/main/java/edu/harvard/iq/dataverse/api/Files.java b/src/main/java/edu/harvard/iq/dataverse/api/Files.java index c30503199e0..ed331e6835d 100644 --- a/src/main/java/edu/harvard/iq/dataverse/api/Files.java +++ b/src/main/java/edu/harvard/iq/dataverse/api/Files.java @@ -953,10 +953,11 @@ public Response getFileCitationByVersion(@Context ContainerRequestContext crc, @ final DataFile df = execCommand(new GetDataFileCommand(req, findDataFileOrDie(fileIdOrPersistentId))); Dataset ds = df.getOwner(); // Adapted from getDatasetVersionOrDie - // includeDeaccessioned and checkPermsWhenDeaccessioned were removed - // because they aren't needed. DatasetVersion dsv = execCommand(handleVersion(dsVersionString, new Datasets.DsVersionHandler>() { + boolean includeDeaccessioned = true; + boolean checkPermsWhenDeaccessioned = true; + @Override public Command handleLatest() { return new GetLatestAccessibleDatasetVersionCommand(req, ds); @@ -969,7 +970,7 @@ public Command handleDraft() { @Override public Command handleSpecific(long major, long minor) { - return new GetSpecificPublishedDatasetVersionCommand(req, ds, major, minor); + return new GetSpecificPublishedDatasetVersionCommand(req, ds, major, minor, includeDeaccessioned, checkPermsWhenDeaccessioned); } @Override @@ -978,6 +979,10 @@ public Command handleLatestPublished() { } })); + if (dsv == null) { + return unauthorized("Dataset version cannot be found or unauthorized."); + } + Long getDatasetVersionID = dsv.getId(); FileMetadata fm = dataFileServiceBean.findFileMetadataByDatasetVersionIdAndDataFileId(getDatasetVersionID, df.getId()); if (fm == null) { diff --git a/src/test/java/edu/harvard/iq/dataverse/api/FilesIT.java b/src/test/java/edu/harvard/iq/dataverse/api/FilesIT.java index 4bc7456e7e7..1e8a806faa2 100644 --- a/src/test/java/edu/harvard/iq/dataverse/api/FilesIT.java +++ b/src/test/java/edu/harvard/iq/dataverse/api/FilesIT.java @@ -2487,7 +2487,7 @@ public void testCollectionStorageQuotas() { } @Test - public void getFileCitation() throws IOException { + public void testFileCitation() throws IOException { Response createUser = UtilIT.createRandomUser(); createUser.then().assertThat().statusCode(OK.getStatusCode()); String apiToken = UtilIT.getApiTokenFromResponse(createUser); @@ -2570,11 +2570,37 @@ public void getFileCitation() throws IOException { .statusCode(OK.getStatusCode()) .body("data.message", equalTo("Finch, Fiona, " + currentYear + ", \"Darwin's Finches\", " + pidAsUrl + ", Root, DRAFT VERSION; foo.txt [fileName]")); - Response getFileCitationV1Filename = UtilIT.getFileCitation(fileId, "1.0", apiToken); - getFileCitationV1Filename.prettyPrint(); - getFileCitationV1Filename.then().assertThat() + Response getFileCitationV1OldFilename = UtilIT.getFileCitation(fileId, "1.0", apiToken); + getFileCitationV1OldFilename.prettyPrint(); + getFileCitationV1OldFilename.then().assertThat() .statusCode(OK.getStatusCode()) .body("data.message", equalTo("Finch, Fiona, " + currentYear + ", \"Darwin's Finches\", " + pidAsUrl + ", Root, V1; file.txt [fileName]")); + + UtilIT.publishDatasetViaNativeApi(datasetId, "major", apiToken) + .then().assertThat().statusCode(OK.getStatusCode()); + + Response deaccessionDataset = UtilIT.deaccessionDataset(datasetId, "1.0", "just because", "http://example.com", apiToken); + deaccessionDataset.prettyPrint(); + deaccessionDataset.then().assertThat().statusCode(OK.getStatusCode()); + + Response getFileCitationV1PostDeaccessionAuthor = UtilIT.getFileCitation(fileId, "1.0", apiToken); + getFileCitationV1PostDeaccessionAuthor.prettyPrint(); + getFileCitationV1PostDeaccessionAuthor.then().assertThat() + .statusCode(OK.getStatusCode()) + .body("data.message", equalTo("Finch, Fiona, " + currentYear + ", \"Darwin's Finches\", " + pidAsUrl + ", Root, V1, DEACCESSIONED VERSION; file.txt [fileName]")); + + Response getFileCitationV1PostDeaccessionNoApiToken = UtilIT.getFileCitation(fileId, "1.0", null); + getFileCitationV1PostDeaccessionNoApiToken.prettyPrint(); + getFileCitationV1PostDeaccessionNoApiToken.then().assertThat() + .statusCode(UNAUTHORIZED.getStatusCode()) + .body("message", equalTo("Dataset version cannot be found or unauthorized.")); + + Response getFileCitationV1PostDeaccessionNoPermsUser = UtilIT.getFileCitation(fileId, "1.0", noPermsApiToken); + getFileCitationV1PostDeaccessionNoPermsUser.prettyPrint(); + getFileCitationV1PostDeaccessionNoPermsUser.then().assertThat() + .statusCode(UNAUTHORIZED.getStatusCode()) + .body("message", equalTo("Dataset version cannot be found or unauthorized.")); + } } From d40ecfd420bc34df884a6b4e820946f0f457c6be Mon Sep 17 00:00:00 2001 From: Philip Durbin Date: Tue, 23 Jan 2024 13:57:09 -0500 Subject: [PATCH 05/13] add docs for "get file citation" API #10240 --- doc/sphinx-guides/source/api/native-api.rst | 53 ++++++++++++++++++++- 1 file changed, 52 insertions(+), 1 deletion(-) diff --git a/doc/sphinx-guides/source/api/native-api.rst b/doc/sphinx-guides/source/api/native-api.rst index dbe769e2fd1..f161dd67ca9 100644 --- a/doc/sphinx-guides/source/api/native-api.rst +++ b/doc/sphinx-guides/source/api/native-api.rst @@ -845,7 +845,12 @@ Datasets **Note** Creation of new datasets is done with a ``POST`` onto a Dataverse collection. See the Dataverse Collections section above. -**Note** In all commands below, dataset versions can be referred to as: +.. _dataset-version-specifiers: + +Dataset Version Specifiers +~~~~~~~~~~~~~~~~~~~~~~~~~~ + +In all commands below, dataset versions can be referred to as: * ``:draft`` the draft version, if any * ``:latest`` either a draft (if exists) or the latest published version. @@ -2712,6 +2717,8 @@ The fully expanded example above (without environment variables) looks like this Files ----- +.. _get-json-rep-of-file: + Get JSON Representation of a File ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -3499,6 +3506,50 @@ The fully expanded example above (without environment variables) looks like this You can download :download:`dct.xml <../../../../src/test/resources/xml/dct.xml>` from the example above to see what the XML looks like. +Get File Citation as JSON +~~~~~~~~~~~~~~~~~~~~~~~~~ + +This API is for getting the file citation as it appears on the file landing page. It is formatted in HTML and encoded in JSON. + +To specify the version, you can use ``:latest-published`` or ``:draft`` or ``1.0`` or any other style listed under :ref:`dataset-version-specifiers`. + +When the dataset version is published, authentication is not required: + +.. code-block:: bash + + export SERVER_URL=https://demo.dataverse.org + export FILE_ID=42 + export DATASET_VERSION=":latest-published" + + curl "$SERVER_URL/api/files/$FILE_ID/versions/$DATASET_VERSION/citation" + +The fully expanded example above (without environment variables) looks like this: + +.. code-block:: bash + + curl "https://demo.dataverse.org/api/files/42/versions/:latest-published/citation" + +When the dataset version is a draft or deaccessioned, authentication is required: + +.. code-block:: bash + + export API_TOKEN=xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx + export SERVER_URL=https://demo.dataverse.org + export FILE_ID=42 + export DATASET_VERSION=":draft" + + curl -H "X-Dataverse-key:$API_TOKEN" "$SERVER_URL/api/files/$FILE_ID/versions/$DATASET_VERSION/citation" + +The fully expanded example above (without environment variables) looks like this: + +.. code-block:: bash + + curl -H "X-Dataverse-key:xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx" https://demo.dataverse.org/api/files/42/versions/:draft/citation + +If your file has a persistent identifier (PID, such as a DOI), you can pass it using the technique described under :ref:`get-json-rep-of-file`. + +This API is not for downloading various citation formats such as EndNote XML, RIS, or BibTeX. This functionality has been requested in https://github.com/IQSS/dataverse/issues/3140 and https://github.com/IQSS/dataverse/issues/9994. + Provenance ~~~~~~~~~~ From 521afc50d807aacb39a74166c303d61fe5f64b2d Mon Sep 17 00:00:00 2001 From: Philip Durbin Date: Tue, 23 Jan 2024 13:59:32 -0500 Subject: [PATCH 06/13] add release note for "get file citation" API #10240 --- doc/release-notes/10240-file-citation.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 doc/release-notes/10240-file-citation.md diff --git a/doc/release-notes/10240-file-citation.md b/doc/release-notes/10240-file-citation.md new file mode 100644 index 00000000000..fb747527669 --- /dev/null +++ b/doc/release-notes/10240-file-citation.md @@ -0,0 +1,5 @@ +## Get file citation as JSON + +It is now possible to retrieve via API the file citation as it appears on the file landing page. It is formatted in HTML and encoded in JSON. + +This API is not for downloading various citation formats such as EndNote XML, RIS, or BibTeX. This functionality has been requested in https://github.com/IQSS/dataverse/issues/3140 and https://github.com/IQSS/dataverse/issues/9994 From 99555017b3890c3d7c8c244ee5bae92f7ec962da Mon Sep 17 00:00:00 2001 From: Philip Durbin Date: Wed, 7 Feb 2024 10:16:32 -0500 Subject: [PATCH 07/13] remove superflous double quotes #10240 --- doc/sphinx-guides/source/api/native-api.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/sphinx-guides/source/api/native-api.rst b/doc/sphinx-guides/source/api/native-api.rst index f161dd67ca9..d6f88df3235 100644 --- a/doc/sphinx-guides/source/api/native-api.rst +++ b/doc/sphinx-guides/source/api/native-api.rst @@ -3536,7 +3536,7 @@ When the dataset version is a draft or deaccessioned, authentication is required export API_TOKEN=xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx export SERVER_URL=https://demo.dataverse.org export FILE_ID=42 - export DATASET_VERSION=":draft" + export DATASET_VERSION=:draft curl -H "X-Dataverse-key:$API_TOKEN" "$SERVER_URL/api/files/$FILE_ID/versions/$DATASET_VERSION/citation" From 316524a45e85ca359357b568774dd1493e913e2e Mon Sep 17 00:00:00 2001 From: Philip Durbin Date: Wed, 7 Feb 2024 10:26:48 -0500 Subject: [PATCH 08/13] move English to bundle #10240 --- src/main/java/edu/harvard/iq/dataverse/api/Files.java | 4 ++-- src/main/java/propertyFiles/Bundle.properties | 2 ++ 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/main/java/edu/harvard/iq/dataverse/api/Files.java b/src/main/java/edu/harvard/iq/dataverse/api/Files.java index ed331e6835d..f7cdf2df10b 100644 --- a/src/main/java/edu/harvard/iq/dataverse/api/Files.java +++ b/src/main/java/edu/harvard/iq/dataverse/api/Files.java @@ -980,13 +980,13 @@ public Command handleLatestPublished() { })); if (dsv == null) { - return unauthorized("Dataset version cannot be found or unauthorized."); + return unauthorized(BundleUtil.getStringFromBundle("files.api.no.draftOrUnauth")); } Long getDatasetVersionID = dsv.getId(); FileMetadata fm = dataFileServiceBean.findFileMetadataByDatasetVersionIdAndDataFileId(getDatasetVersionID, df.getId()); if (fm == null) { - return notFound("File could not be found."); + return notFound(BundleUtil.getStringFromBundle("files.api.fileNotFound")); } boolean direct = false; DataCitation citation = new DataCitation(fm, direct); diff --git a/src/main/java/propertyFiles/Bundle.properties b/src/main/java/propertyFiles/Bundle.properties index 157f2ecaf54..5ecab876e01 100644 --- a/src/main/java/propertyFiles/Bundle.properties +++ b/src/main/java/propertyFiles/Bundle.properties @@ -2633,7 +2633,9 @@ admin.api.deleteUser.success=Authenticated User {0} deleted. #Files.java files.api.metadata.update.duplicateFile=Filename already exists at {0} files.api.no.draft=No draft available for this file +files.api.no.draftOrUnauth=Dataset version cannot be found or unauthorized. files.api.only.tabular.supported=This operation is only available for tabular files. +files.api.fileNotFound=File could not be found. #Datasets.java datasets.api.updatePIDMetadata.failure.dataset.must.be.released=Modify Registration Metadata must be run on a published dataset. From e9ab40bdaadb9246a8d191ff6c76ba6dc16762e5 Mon Sep 17 00:00:00 2001 From: Philip Durbin Date: Wed, 7 Feb 2024 10:44:16 -0500 Subject: [PATCH 09/13] simplify and rename tests #10240 --- .../edu/harvard/iq/dataverse/api/FilesIT.java | 33 ++++++------------- 1 file changed, 10 insertions(+), 23 deletions(-) diff --git a/src/test/java/edu/harvard/iq/dataverse/api/FilesIT.java b/src/test/java/edu/harvard/iq/dataverse/api/FilesIT.java index 1e8a806faa2..53f8aa40e4a 100644 --- a/src/test/java/edu/harvard/iq/dataverse/api/FilesIT.java +++ b/src/test/java/edu/harvard/iq/dataverse/api/FilesIT.java @@ -2487,7 +2487,7 @@ public void testCollectionStorageQuotas() { } @Test - public void testFileCitation() throws IOException { + public void testFileCitationByVersion() throws IOException { Response createUser = UtilIT.createRandomUser(); createUser.then().assertThat().statusCode(OK.getStatusCode()); String apiToken = UtilIT.getApiTokenFromResponse(createUser); @@ -2502,24 +2502,11 @@ public void testFileCitation() throws IOException { Integer datasetId = JsonPath.from(createDatasetResponse.body().asString()).getInt("data.id"); String datasetPid = JsonPath.from(createDatasetResponse.body().asString()).getString("data.persistentId"); - Response getDatasetVersionCitationResponse = UtilIT.getDatasetVersionCitation(datasetId, DS_VERSION_DRAFT, false, apiToken); - getDatasetVersionCitationResponse.prettyPrint(); - getDatasetVersionCitationResponse.then().assertThat() - .statusCode(OK.getStatusCode()) - // We check that the returned message contains information expected for the citation string - .body("data.message", containsString("DRAFT VERSION")); - - Path pathToTxt = Paths.get(java.nio.file.Files.createTempDirectory(null) + File.separator + "file.txt"); - String contentOfTxt = "foobar"; - java.nio.file.Files.write(pathToTxt, contentOfTxt.getBytes()); - - Response uploadFileTxt = UtilIT.uploadFileViaNative(datasetId.toString(), pathToTxt.toString(), apiToken); - uploadFileTxt.prettyPrint(); - uploadFileTxt.then().assertThat() - .statusCode(OK.getStatusCode()) - .body("data.files[0].label", equalTo("file.txt")); + String pathToTestFile = "src/test/resources/images/coffeeshop.png"; + Response uploadFile = UtilIT.uploadFileViaNative(datasetId.toString(), pathToTestFile, Json.createObjectBuilder().build(), apiToken); + uploadFile.then().assertThat().statusCode(OK.getStatusCode()); - Integer fileId = JsonPath.from(uploadFileTxt.body().asString()).getInt("data.files[0].dataFile.id"); + Integer fileId = JsonPath.from(uploadFile.body().asString()).getInt("data.files[0].dataFile.id"); String pidAsUrl = "https://doi.org/" + datasetPid.split("doi:")[1]; int currentYear = Year.now().getValue(); @@ -2540,7 +2527,7 @@ public void testFileCitation() throws IOException { getFileCitationDraft.prettyPrint(); getFileCitationDraft.then().assertThat() .statusCode(OK.getStatusCode()) - .body("data.message", equalTo("Finch, Fiona, " + currentYear + ", \"Darwin's Finches\", " + pidAsUrl + ", Root, DRAFT VERSION; file.txt [fileName]")); + .body("data.message", equalTo("Finch, Fiona, " + currentYear + ", \"Darwin's Finches\", " + pidAsUrl + ", Root, DRAFT VERSION; coffeeshop.png [fileName]")); Response publishDataverseResponse = UtilIT.publishDataverseViaNativeApi(dataverseAlias, apiToken); publishDataverseResponse.then().assertThat().statusCode(OK.getStatusCode()); @@ -2556,7 +2543,7 @@ public void testFileCitation() throws IOException { String updateJsonString = """ { - "label": "foo.txt" + "label": "foo.png" } """; @@ -2568,13 +2555,13 @@ public void testFileCitation() throws IOException { getFileCitationPostV1Draft.prettyPrint(); getFileCitationPostV1Draft.then().assertThat() .statusCode(OK.getStatusCode()) - .body("data.message", equalTo("Finch, Fiona, " + currentYear + ", \"Darwin's Finches\", " + pidAsUrl + ", Root, DRAFT VERSION; foo.txt [fileName]")); + .body("data.message", equalTo("Finch, Fiona, " + currentYear + ", \"Darwin's Finches\", " + pidAsUrl + ", Root, DRAFT VERSION; foo.png [fileName]")); Response getFileCitationV1OldFilename = UtilIT.getFileCitation(fileId, "1.0", apiToken); getFileCitationV1OldFilename.prettyPrint(); getFileCitationV1OldFilename.then().assertThat() .statusCode(OK.getStatusCode()) - .body("data.message", equalTo("Finch, Fiona, " + currentYear + ", \"Darwin's Finches\", " + pidAsUrl + ", Root, V1; file.txt [fileName]")); + .body("data.message", equalTo("Finch, Fiona, " + currentYear + ", \"Darwin's Finches\", " + pidAsUrl + ", Root, V1; coffeeshop.png [fileName]")); UtilIT.publishDatasetViaNativeApi(datasetId, "major", apiToken) .then().assertThat().statusCode(OK.getStatusCode()); @@ -2587,7 +2574,7 @@ public void testFileCitation() throws IOException { getFileCitationV1PostDeaccessionAuthor.prettyPrint(); getFileCitationV1PostDeaccessionAuthor.then().assertThat() .statusCode(OK.getStatusCode()) - .body("data.message", equalTo("Finch, Fiona, " + currentYear + ", \"Darwin's Finches\", " + pidAsUrl + ", Root, V1, DEACCESSIONED VERSION; file.txt [fileName]")); + .body("data.message", equalTo("Finch, Fiona, " + currentYear + ", \"Darwin's Finches\", " + pidAsUrl + ", Root, V1, DEACCESSIONED VERSION; coffeeshop.png [fileName]")); Response getFileCitationV1PostDeaccessionNoApiToken = UtilIT.getFileCitation(fileId, "1.0", null); getFileCitationV1PostDeaccessionNoApiToken.prettyPrint(); From a2194d9ce6229072a19f1ccd5a13a70e6546f447 Mon Sep 17 00:00:00 2001 From: Philip Durbin Date: Wed, 7 Feb 2024 10:48:37 -0500 Subject: [PATCH 10/13] stop using var; make consistent with older method #10240 --- src/test/java/edu/harvard/iq/dataverse/api/UtilIT.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) 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 b5957a756d3..b51d6af75a9 100644 --- a/src/test/java/edu/harvard/iq/dataverse/api/UtilIT.java +++ b/src/test/java/edu/harvard/iq/dataverse/api/UtilIT.java @@ -3475,11 +3475,11 @@ static Response getDatasetVersionCitation(Integer datasetId, String version, boo } static Response getFileCitation(Integer fileId, String datasetVersion, String apiToken) { - var spec = given(); + RequestSpecification requestSpecification = given(); if (apiToken != null) { - spec.header(API_TOKEN_HTTP_HEADER, apiToken); + requestSpecification.header(API_TOKEN_HTTP_HEADER, apiToken); } - return spec.get("/api/files/" + fileId + "/versions/" + datasetVersion + "/citation"); + return requestSpecification.get("/api/files/" + fileId + "/versions/" + datasetVersion + "/citation"); } static Response getVersionFiles(Integer datasetId, From 4542b213103ecc18cbf50617696c2997a2a9723d Mon Sep 17 00:00:00 2001 From: Philip Durbin Date: Wed, 7 Feb 2024 16:36:49 -0500 Subject: [PATCH 11/13] refactor handleVersion, add includeDeaccession param #10240 --- doc/sphinx-guides/source/api/native-api.rst | 11 ++++-- .../iq/dataverse/api/AbstractApiBean.java | 31 +++++++++++++++ .../harvard/iq/dataverse/api/Datasets.java | 23 +---------- .../edu/harvard/iq/dataverse/api/Files.java | 38 ++----------------- .../edu/harvard/iq/dataverse/api/FilesIT.java | 11 ++++-- .../edu/harvard/iq/dataverse/api/UtilIT.java | 8 ++++ 6 files changed, 60 insertions(+), 62 deletions(-) diff --git a/doc/sphinx-guides/source/api/native-api.rst b/doc/sphinx-guides/source/api/native-api.rst index d6f88df3235..5be73c01194 100644 --- a/doc/sphinx-guides/source/api/native-api.rst +++ b/doc/sphinx-guides/source/api/native-api.rst @@ -3529,7 +3529,11 @@ The fully expanded example above (without environment variables) looks like this curl "https://demo.dataverse.org/api/files/42/versions/:latest-published/citation" -When the dataset version is a draft or deaccessioned, authentication is required: +When the dataset version is a draft or deaccessioned, authentication is required. + +By default, deaccessioned dataset versions are not included in the search when applying the :latest or :latest-published identifiers. Additionally, when filtering by a specific version tag, you will get a "unauthorized" error if the version is deaccessioned and you do not enable the ``includeDeaccessioned`` option described below. + +If you want to include deaccessioned dataset versions, you must set ``includeDeaccessioned`` query parameter to ``true``. .. code-block:: bash @@ -3537,14 +3541,15 @@ When the dataset version is a draft or deaccessioned, authentication is required export SERVER_URL=https://demo.dataverse.org export FILE_ID=42 export DATASET_VERSION=:draft + export INCLUDE_DEACCESSIONED=true - curl -H "X-Dataverse-key:$API_TOKEN" "$SERVER_URL/api/files/$FILE_ID/versions/$DATASET_VERSION/citation" + curl -H "X-Dataverse-key:$API_TOKEN" "$SERVER_URL/api/files/$FILE_ID/versions/$DATASET_VERSION/citation?includeDeaccessioned=$INCLUDE_DEACCESSIONED" The fully expanded example above (without environment variables) looks like this: .. code-block:: bash - curl -H "X-Dataverse-key:xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx" https://demo.dataverse.org/api/files/42/versions/:draft/citation + curl -H "X-Dataverse-key:xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx" "https://demo.dataverse.org/api/files/42/versions/:draft/citation?includeDeaccessioned=true" If your file has a persistent identifier (PID, such as a DOI), you can pass it using the technique described under :ref:`get-json-rep-of-file`. diff --git a/src/main/java/edu/harvard/iq/dataverse/api/AbstractApiBean.java b/src/main/java/edu/harvard/iq/dataverse/api/AbstractApiBean.java index bc94d7f0bcc..3c3e68c4e44 100644 --- a/src/main/java/edu/harvard/iq/dataverse/api/AbstractApiBean.java +++ b/src/main/java/edu/harvard/iq/dataverse/api/AbstractApiBean.java @@ -2,6 +2,7 @@ import edu.harvard.iq.dataverse.*; import edu.harvard.iq.dataverse.actionlogging.ActionLogServiceBean; +import static edu.harvard.iq.dataverse.api.Datasets.handleVersion; import edu.harvard.iq.dataverse.authorization.AuthenticationServiceBean; import edu.harvard.iq.dataverse.authorization.DataverseRole; import edu.harvard.iq.dataverse.authorization.RoleAssignee; @@ -15,6 +16,10 @@ import edu.harvard.iq.dataverse.engine.command.exception.CommandException; import edu.harvard.iq.dataverse.engine.command.exception.IllegalCommandException; import edu.harvard.iq.dataverse.engine.command.exception.PermissionException; +import edu.harvard.iq.dataverse.engine.command.impl.GetDraftDatasetVersionCommand; +import edu.harvard.iq.dataverse.engine.command.impl.GetLatestAccessibleDatasetVersionCommand; +import edu.harvard.iq.dataverse.engine.command.impl.GetLatestPublishedDatasetVersionCommand; +import edu.harvard.iq.dataverse.engine.command.impl.GetSpecificPublishedDatasetVersionCommand; import edu.harvard.iq.dataverse.externaltools.ExternalToolServiceBean; import edu.harvard.iq.dataverse.license.LicenseServiceBean; import edu.harvard.iq.dataverse.locality.StorageSiteServiceBean; @@ -390,6 +395,32 @@ protected Dataset findDatasetOrDie(String id) throws WrappedResponse { } } } + + protected DatasetVersion findDatasetVersionOrDie(final DataverseRequest req, String versionNumber, final Dataset ds, boolean includeDeaccessioned, boolean checkPermsWhenDeaccessioned) throws WrappedResponse { + DatasetVersion dsv = execCommand(handleVersion(versionNumber, new Datasets.DsVersionHandler>() { + + @Override + public Command handleLatest() { + return new GetLatestAccessibleDatasetVersionCommand(req, ds, includeDeaccessioned, checkPermsWhenDeaccessioned); + } + + @Override + public Command handleDraft() { + return new GetDraftDatasetVersionCommand(req, ds); + } + + @Override + public Command handleSpecific(long major, long minor) { + return new GetSpecificPublishedDatasetVersionCommand(req, ds, major, minor, includeDeaccessioned, checkPermsWhenDeaccessioned); + } + + @Override + public Command handleLatestPublished() { + return new GetLatestPublishedDatasetVersionCommand(req, ds, includeDeaccessioned, checkPermsWhenDeaccessioned); + } + })); + return dsv; + } protected DataFile findDataFileOrDie(String id) throws WrappedResponse { diff --git a/src/main/java/edu/harvard/iq/dataverse/api/Datasets.java b/src/main/java/edu/harvard/iq/dataverse/api/Datasets.java index e3505cbbb33..2181f189fc0 100644 --- a/src/main/java/edu/harvard/iq/dataverse/api/Datasets.java +++ b/src/main/java/edu/harvard/iq/dataverse/api/Datasets.java @@ -2727,28 +2727,7 @@ private DatasetVersion getDatasetVersionOrDie(final DataverseRequest req, String * Will allow to define when the permissions should be checked when a deaccesioned dataset is requested. If the user doesn't have edit permissions will result in an error. */ private DatasetVersion getDatasetVersionOrDie(final DataverseRequest req, String versionNumber, final Dataset ds, UriInfo uriInfo, HttpHeaders headers, boolean includeDeaccessioned, boolean checkPermsWhenDeaccessioned) throws WrappedResponse { - DatasetVersion dsv = execCommand(handleVersion(versionNumber, new DsVersionHandler>() { - - @Override - public Command handleLatest() { - return new GetLatestAccessibleDatasetVersionCommand(req, ds, includeDeaccessioned, checkPermsWhenDeaccessioned); - } - - @Override - public Command handleDraft() { - return new GetDraftDatasetVersionCommand(req, ds); - } - - @Override - public Command handleSpecific(long major, long minor) { - return new GetSpecificPublishedDatasetVersionCommand(req, ds, major, minor, includeDeaccessioned, checkPermsWhenDeaccessioned); - } - - @Override - public Command handleLatestPublished() { - return new GetLatestPublishedDatasetVersionCommand(req, ds, includeDeaccessioned, checkPermsWhenDeaccessioned); - } - })); + DatasetVersion dsv = findDatasetVersionOrDie(req, versionNumber, ds, includeDeaccessioned, checkPermsWhenDeaccessioned); if (dsv == null || dsv.getId() == null) { throw new WrappedResponse(notFound("Dataset version " + versionNumber + " of dataset " + ds.getId() + " not found")); } diff --git a/src/main/java/edu/harvard/iq/dataverse/api/Files.java b/src/main/java/edu/harvard/iq/dataverse/api/Files.java index f7cdf2df10b..69bdebb2dd5 100644 --- a/src/main/java/edu/harvard/iq/dataverse/api/Files.java +++ b/src/main/java/edu/harvard/iq/dataverse/api/Files.java @@ -19,7 +19,6 @@ import edu.harvard.iq.dataverse.GuestbookResponseServiceBean; import edu.harvard.iq.dataverse.TermsOfUseAndAccessValidator; import edu.harvard.iq.dataverse.UserNotificationServiceBean; -import static edu.harvard.iq.dataverse.api.Datasets.handleVersion; import edu.harvard.iq.dataverse.api.auth.AuthRequired; import edu.harvard.iq.dataverse.authorization.Permission; import edu.harvard.iq.dataverse.authorization.users.ApiToken; @@ -34,11 +33,7 @@ import edu.harvard.iq.dataverse.engine.command.exception.CommandException; import edu.harvard.iq.dataverse.engine.command.exception.IllegalCommandException; import edu.harvard.iq.dataverse.engine.command.impl.GetDataFileCommand; -import edu.harvard.iq.dataverse.engine.command.impl.GetDraftDatasetVersionCommand; import edu.harvard.iq.dataverse.engine.command.impl.GetDraftFileMetadataIfAvailableCommand; -import edu.harvard.iq.dataverse.engine.command.impl.GetLatestAccessibleDatasetVersionCommand; -import edu.harvard.iq.dataverse.engine.command.impl.GetLatestPublishedDatasetVersionCommand; -import edu.harvard.iq.dataverse.engine.command.impl.GetSpecificPublishedDatasetVersionCommand; import edu.harvard.iq.dataverse.engine.command.impl.RedetectFileTypeCommand; import edu.harvard.iq.dataverse.engine.command.impl.RestrictFileCommand; import edu.harvard.iq.dataverse.engine.command.impl.UningestFileCommand; @@ -941,44 +936,19 @@ public Response getHasBeenDeleted(@Context ContainerRequestContext crc, @PathPar /** * @param fileIdOrPersistentId Database ID or PID of the data file. - * @param dsVersionString The version of the dataset, such as 1.0, :draft, + * @param versionNumber The version of the dataset, such as 1.0, :draft, * :latest-published, etc. + * @param includeDeaccessioned Defaults to false. */ @GET @AuthRequired @Path("{id}/versions/{dsVersionString}/citation") - public Response getFileCitationByVersion(@Context ContainerRequestContext crc, @PathParam("id") String fileIdOrPersistentId, @PathParam("dsVersionString") String dsVersionString) { + public Response getFileCitationByVersion(@Context ContainerRequestContext crc, @PathParam("id") String fileIdOrPersistentId, @PathParam("dsVersionString") String versionNumber, @QueryParam("includeDeaccessioned") boolean includeDeaccessioned) { try { DataverseRequest req = createDataverseRequest(getRequestUser(crc)); final DataFile df = execCommand(new GetDataFileCommand(req, findDataFileOrDie(fileIdOrPersistentId))); Dataset ds = df.getOwner(); - // Adapted from getDatasetVersionOrDie - DatasetVersion dsv = execCommand(handleVersion(dsVersionString, new Datasets.DsVersionHandler>() { - - boolean includeDeaccessioned = true; - boolean checkPermsWhenDeaccessioned = true; - - @Override - public Command handleLatest() { - return new GetLatestAccessibleDatasetVersionCommand(req, ds); - } - - @Override - public Command handleDraft() { - return new GetDraftDatasetVersionCommand(req, ds); - } - - @Override - public Command handleSpecific(long major, long minor) { - return new GetSpecificPublishedDatasetVersionCommand(req, ds, major, minor, includeDeaccessioned, checkPermsWhenDeaccessioned); - } - - @Override - public Command handleLatestPublished() { - return new GetLatestPublishedDatasetVersionCommand(req, ds); - } - })); - + DatasetVersion dsv = findDatasetVersionOrDie(req, versionNumber, ds, includeDeaccessioned, true); if (dsv == null) { return unauthorized(BundleUtil.getStringFromBundle("files.api.no.draftOrUnauth")); } diff --git a/src/test/java/edu/harvard/iq/dataverse/api/FilesIT.java b/src/test/java/edu/harvard/iq/dataverse/api/FilesIT.java index f30d7e803ee..dbdb12a3e8d 100644 --- a/src/test/java/edu/harvard/iq/dataverse/api/FilesIT.java +++ b/src/test/java/edu/harvard/iq/dataverse/api/FilesIT.java @@ -2698,9 +2698,14 @@ public void testFileCitationByVersion() throws IOException { deaccessionDataset.prettyPrint(); deaccessionDataset.then().assertThat().statusCode(OK.getStatusCode()); - Response getFileCitationV1PostDeaccessionAuthor = UtilIT.getFileCitation(fileId, "1.0", apiToken); - getFileCitationV1PostDeaccessionAuthor.prettyPrint(); - getFileCitationV1PostDeaccessionAuthor.then().assertThat() + Response getFileCitationV1PostDeaccessionAuthorDefault = UtilIT.getFileCitation(fileId, "1.0", apiToken); + getFileCitationV1PostDeaccessionAuthorDefault.prettyPrint(); + getFileCitationV1PostDeaccessionAuthorDefault.then().assertThat() + .statusCode(UNAUTHORIZED.getStatusCode()); + + Response getFileCitationV1PostDeaccessionAuthorIncludeDeaccessioned = UtilIT.getFileCitation(fileId, "1.0", true, apiToken); + getFileCitationV1PostDeaccessionAuthorIncludeDeaccessioned.prettyPrint(); + getFileCitationV1PostDeaccessionAuthorIncludeDeaccessioned.then().assertThat() .statusCode(OK.getStatusCode()) .body("data.message", equalTo("Finch, Fiona, " + currentYear + ", \"Darwin's Finches\", " + pidAsUrl + ", Root, V1, DEACCESSIONED VERSION; coffeeshop.png [fileName]")); 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 b51d6af75a9..f307275af1f 100644 --- a/src/test/java/edu/harvard/iq/dataverse/api/UtilIT.java +++ b/src/test/java/edu/harvard/iq/dataverse/api/UtilIT.java @@ -3475,10 +3475,18 @@ static Response getDatasetVersionCitation(Integer datasetId, String version, boo } static Response getFileCitation(Integer fileId, String datasetVersion, String apiToken) { + Boolean includeDeaccessioned = null; + return getFileCitation(fileId, datasetVersion, includeDeaccessioned, apiToken); + } + + static Response getFileCitation(Integer fileId, String datasetVersion, Boolean includeDeaccessioned, String apiToken) { RequestSpecification requestSpecification = given(); if (apiToken != null) { requestSpecification.header(API_TOKEN_HTTP_HEADER, apiToken); } + if (includeDeaccessioned != null) { + requestSpecification.queryParam("includeDeaccessioned", includeDeaccessioned); + } return requestSpecification.get("/api/files/" + fileId + "/versions/" + datasetVersion + "/citation"); } From 244cb1a7a3ed87cc747ede3bd7da967e6f5e2938 Mon Sep 17 00:00:00 2001 From: Philip Durbin Date: Fri, 9 Feb 2024 12:24:58 -0500 Subject: [PATCH 12/13] support citation for files with PIDs #10240 --- .../edu/harvard/iq/dataverse/api/Files.java | 2 +- .../iq/dataverse/DataCitationTest.java | 31 +++++++++++++++++++ 2 files changed, 32 insertions(+), 1 deletion(-) diff --git a/src/main/java/edu/harvard/iq/dataverse/api/Files.java b/src/main/java/edu/harvard/iq/dataverse/api/Files.java index 69bdebb2dd5..440577d1518 100644 --- a/src/main/java/edu/harvard/iq/dataverse/api/Files.java +++ b/src/main/java/edu/harvard/iq/dataverse/api/Files.java @@ -958,7 +958,7 @@ public Response getFileCitationByVersion(@Context ContainerRequestContext crc, @ if (fm == null) { return notFound(BundleUtil.getStringFromBundle("files.api.fileNotFound")); } - boolean direct = false; + boolean direct = df.isIdentifierRegistered(); DataCitation citation = new DataCitation(fm, direct); return ok(citation.toString(true)); } catch (WrappedResponse ex) { diff --git a/src/test/java/edu/harvard/iq/dataverse/DataCitationTest.java b/src/test/java/edu/harvard/iq/dataverse/DataCitationTest.java index 4097adb0be6..23a7efedca7 100644 --- a/src/test/java/edu/harvard/iq/dataverse/DataCitationTest.java +++ b/src/test/java/edu/harvard/iq/dataverse/DataCitationTest.java @@ -378,6 +378,36 @@ public void testTitleWithQuotes() throws ParseException { } + @Test + public void testFileCitationToStringHtml() throws ParseException { + DatasetVersion dsv = createATestDatasetVersion("Dataset Title", true); + FileMetadata fileMetadata = new FileMetadata(); + fileMetadata.setLabel("foo.txt"); + fileMetadata.setDataFile(new DataFile()); + dsv.setVersionState(DatasetVersion.VersionState.RELEASED); + fileMetadata.setDatasetVersion(dsv); + dsv.setDataset(dsv.getDataset()); + DataCitation fileCitation = new DataCitation(fileMetadata, false); + assertEquals("First Last, 1955, \"Dataset Title\", https://doi.org/10.5072/FK2/LK0D1H, LibraScholar, V1; foo.txt [fileName]", fileCitation.toString(true)); + } + + @Test + public void testFileCitationToStringHtmlFilePid() throws ParseException { + DatasetVersion dsv = createATestDatasetVersion("Dataset Title", true); + FileMetadata fileMetadata = new FileMetadata(); + fileMetadata.setLabel("foo.txt"); + DataFile dataFile = new DataFile(); + dataFile.setProtocol("doi"); + dataFile.setAuthority("10.42"); + dataFile.setIdentifier("myFilePid"); + fileMetadata.setDataFile(dataFile); + dsv.setVersionState(DatasetVersion.VersionState.RELEASED); + fileMetadata.setDatasetVersion(dsv); + dsv.setDataset(dsv.getDataset()); + DataCitation fileCitation = new DataCitation(fileMetadata, true); + assertEquals("First Last, 1955, \"foo.txt\", Dataset Title, https://doi.org/10.42/myFilePid, LibraScholar, V1", fileCitation.toString(true)); + } + private DatasetVersion createATestDatasetVersion(String withTitle, boolean withAuthor) throws ParseException { Dataverse dataverse = new Dataverse(); @@ -400,6 +430,7 @@ private DatasetVersion createATestDatasetVersion(String withTitle, boolean withA fields.add(createTitleField(withTitle)); } if (withAuthor) { + // TODO: "Last, First" would make more sense. fields.add(createAuthorField("First Last")); } From ab0abaf83ca854214bf2a589ab642d4187ffa3fd Mon Sep 17 00:00:00 2001 From: Guillermo Portas Date: Thu, 15 Feb 2024 13:45:34 +0000 Subject: [PATCH 13/13] Removed: double quotes in docs for DATASET_VERSION value --- doc/sphinx-guides/source/api/native-api.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/sphinx-guides/source/api/native-api.rst b/doc/sphinx-guides/source/api/native-api.rst index 5be73c01194..4038ec4340d 100644 --- a/doc/sphinx-guides/source/api/native-api.rst +++ b/doc/sphinx-guides/source/api/native-api.rst @@ -3519,7 +3519,7 @@ When the dataset version is published, authentication is not required: export SERVER_URL=https://demo.dataverse.org export FILE_ID=42 - export DATASET_VERSION=":latest-published" + export DATASET_VERSION=:latest-published curl "$SERVER_URL/api/files/$FILE_ID/versions/$DATASET_VERSION/citation"