Skip to content

Commit 7904e99

Browse files
authored
Merge pull request #12238 from IQSS/11733-api-get-file-citation-format
API: File Get Citation In Other Formats
2 parents 2eb10cc + df16252 commit 7904e99

7 files changed

Lines changed: 168 additions & 37 deletions

File tree

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
### Feature New API ###
2+
New API added to retrieve the DataFile Citation in a requested format. This is similar output to the API to get the Dataset Citation.
3+
4+
SERVER_URL/api/access/datafile/{fileId}/citation/{format}

doc/sphinx-guides/source/api/dataaccess.rst

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -254,6 +254,26 @@ Value Description
254254
ID Exports file with specific file metadata ``ID``.
255255
============== ===========
256256

257+
258+
.. _datafile-citation-formatted-access:
259+
260+
Citation - Get Citation In Other Formats
261+
----------------------------------------
262+
263+
Dataverse can generate datafile citations in "EndNote", "RIS", "BibTeX", and "CSL" formats.
264+
This API call sends the raw format with the appropriate content-type (EndNote is XML, RIS and BibTeX are plain text, and CSL is JSON). ("Internal" is also a valid value, returning the content as HTML).
265+
This API call requires a format in the API call which can be any of the values listed above.
266+
267+
Usage example:
268+
269+
.. code-block:: bash
270+
271+
export SERVER_URL=https://demo.dataverse.org
272+
export DATAFILE_ID=99
273+
export FORMAT=EndNote
274+
275+
curl "$SERVER_URL/api/access/datafile/$DATAFILE_ID/citation/$FORMAT"
276+
257277
.. _data-variable-metadata-access:
258278

259279
Data Variable Metadata Access

doc/sphinx-guides/source/api/native-api.rst

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4182,6 +4182,8 @@ Usage example:
41824182
41834183
The type under CSL can vary based on the dataset type, with "dataset", "software", and "review" as supported values. See also :ref:`dataset-types`.
41844184

4185+
.. note:: You can also get the Datafile Citation by using the Access Datafile API. See: :ref:`datafile-citation-formatted-access`.
4186+
41854187
Get Citation by Preview URL Token
41864188
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
41874189

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

Lines changed: 30 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -5,55 +5,40 @@
55
*/
66
package edu.harvard.iq.dataverse;
77

8+
import de.undercouch.citeproc.csl.CSLItemDataBuilder;
9+
import de.undercouch.citeproc.csl.CSLName;
10+
import de.undercouch.citeproc.csl.CSLNameBuilder;
11+
import de.undercouch.citeproc.csl.CSLType;
12+
import de.undercouch.citeproc.helper.json.JsonBuilder;
13+
import de.undercouch.citeproc.helper.json.StringJsonBuilderFactory;
814
import edu.harvard.iq.dataverse.branding.BrandingUtil;
915
import edu.harvard.iq.dataverse.dataset.DatasetType;
1016
import edu.harvard.iq.dataverse.harvest.client.HarvestingClient;
1117
import edu.harvard.iq.dataverse.pidproviders.AbstractPidProvider;
18+
import edu.harvard.iq.dataverse.util.BundleUtil;
19+
import edu.harvard.iq.dataverse.util.DateUtil;
20+
import edu.harvard.iq.dataverse.util.PersonOrOrgUtil;
21+
import edu.harvard.iq.dataverse.util.SystemConfig;
22+
import edu.harvard.iq.dataverse.util.json.JsonUtil;
23+
import jakarta.ejb.EJBException;
24+
import jakarta.json.JsonObject;
25+
import jakarta.ws.rs.core.MediaType;
26+
import org.apache.commons.lang3.StringUtils;
27+
import org.apache.commons.text.StringEscapeUtils;
1228

13-
import java.io.BufferedWriter;
14-
import java.io.ByteArrayOutputStream;
15-
import java.io.IOException;
16-
import java.io.OutputStream;
17-
import java.io.OutputStreamWriter;
18-
import java.io.Writer;
29+
import javax.xml.stream.XMLOutputFactory;
30+
import javax.xml.stream.XMLStreamException;
31+
import javax.xml.stream.XMLStreamWriter;
32+
import java.io.*;
1933
import java.nio.charset.StandardCharsets;
2034
import java.text.SimpleDateFormat;
21-
import java.util.ArrayList;
22-
import java.util.Date;
23-
import java.util.HashMap;
24-
import java.util.List;
25-
import java.util.Map;
35+
import java.util.*;
2636
import java.util.logging.Level;
2737
import java.util.logging.Logger;
2838
import java.util.regex.Matcher;
2939
import java.util.regex.Pattern;
3040
import java.util.stream.Collectors;
3141

32-
import jakarta.ejb.EJBException;
33-
import jakarta.json.JsonObject;
34-
import jakarta.ws.rs.core.MediaType;
35-
36-
import javax.xml.stream.XMLOutputFactory;
37-
import javax.xml.stream.XMLStreamException;
38-
import javax.xml.stream.XMLStreamWriter;
39-
40-
import edu.harvard.iq.dataverse.util.BundleUtil;
41-
import edu.harvard.iq.dataverse.util.DateUtil;
42-
import edu.harvard.iq.dataverse.util.PersonOrOrgUtil;
43-
import edu.harvard.iq.dataverse.util.SystemConfig;
44-
import edu.harvard.iq.dataverse.util.json.JsonUtil;
45-
46-
import org.apache.commons.text.StringEscapeUtils;
47-
48-
import de.undercouch.citeproc.csl.CSLItemDataBuilder;
49-
import de.undercouch.citeproc.csl.CSLName;
50-
import de.undercouch.citeproc.csl.CSLNameBuilder;
51-
import de.undercouch.citeproc.csl.CSLType;
52-
import de.undercouch.citeproc.helper.json.JsonBuilder;
53-
import de.undercouch.citeproc.helper.json.StringJsonBuilderFactory;
54-
55-
import org.apache.commons.lang3.StringUtils;
56-
5742
import static edu.harvard.iq.dataverse.pidproviders.doi.AbstractDOIProvider.DOI_PROTOCOL;
5843
import static edu.harvard.iq.dataverse.pidproviders.handle.HandlePidProvider.HDL_PROTOCOL;
5944
import static edu.harvard.iq.dataverse.pidproviders.perma.PermaLinkPidProvider.PERMA_PROTOCOL;
@@ -99,6 +84,15 @@ public enum Format {
9984
BibTeX,
10085
CSL
10186
}
87+
88+
public static Format getFormat(String name) {
89+
for (Format format : Format.values()) {
90+
if (format.name().equalsIgnoreCase(name)) {
91+
return format;
92+
}
93+
}
94+
return null;
95+
}
10296

10397
public DataCitation(DatasetVersion dsv) {
10498
this(dsv, false);

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

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -133,7 +133,29 @@ public class Access extends AbstractApiBean {
133133
private static final String DEFAULT_BUNDLE_NAME = "dataverse_files.zip";
134134
private static final int GUESTBOOK_RESPONSE_SIGNEDURL_TIMEOUT_MINUTES = 1;
135135
//@EJB
136-
136+
137+
@GET
138+
@AuthRequired
139+
@Path("datafile/{fileId}/citation/{format}")
140+
public Response datafileCitation(@Context ContainerRequestContext crc,
141+
@PathParam("fileId") String fileId,
142+
@PathParam("format") String formatString) {
143+
144+
DataCitation.Format format = DataCitation.getFormat(formatString);
145+
if (format == null) {
146+
return badRequest(BundleUtil.getStringFromBundle("datasets.api.citation.invalidFormat"));
147+
}
148+
149+
DataFile df = findDataFileOrDieWrapper(fileId);
150+
151+
// This will throw a ForbiddenException if access isn't authorized:
152+
checkAuthorization(crc, df);
153+
154+
String dataCitationFormatted = (new DataCitation(df.getFileMetadata())).toString(format, true, false);
155+
156+
return Response.ok().type(DataCitation.getCitationFormatMediaType(format, true)).entity(dataCitationFormatted).build();
157+
}
158+
137159
// TODO:
138160
// versions? -- L.A. 4.0 beta 10
139161
@GET

src/test/java/edu/harvard/iq/dataverse/api/FilesIT.java

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4055,6 +4055,87 @@ public void testDownloadFileWithGuestbookResponse() throws IOException, JsonPars
40554055
assertEquals(OK.getStatusCode(), signedUrlResponse.getStatusCode());
40564056
}
40574057

4058+
@Test
4059+
public void testGetFileCitationFormatted() {
4060+
Response createUser = UtilIT.createRandomUser();
4061+
createUser.then().assertThat().statusCode(OK.getStatusCode());
4062+
String apiToken = UtilIT.getApiTokenFromResponse(createUser);
4063+
4064+
Response createDataverseResponse = UtilIT.createRandomDataverse(apiToken);
4065+
createDataverseResponse.then().assertThat().statusCode(CREATED.getStatusCode());
4066+
String dataverseAlias = UtilIT.getAliasFromResponse(createDataverseResponse);
4067+
4068+
Response createDatasetResponse = UtilIT.createRandomDatasetViaNativeApi(dataverseAlias, apiToken);
4069+
createDatasetResponse.then().assertThat().statusCode(CREATED.getStatusCode());
4070+
Integer datasetId = JsonPath.from(createDatasetResponse.body().asString()).getInt("data.id");
4071+
4072+
String pathToTestFile = "src/test/resources/images/coffeeshop.png";
4073+
Response uploadFile = UtilIT.uploadFileViaNative(datasetId.toString(), pathToTestFile, Json.createObjectBuilder().build(), apiToken);
4074+
uploadFile.then().assertThat().statusCode(OK.getStatusCode());
4075+
4076+
String fileId = JsonPath.from(uploadFile.body().asString()).getString("data.files[0].dataFile.id");
4077+
4078+
// Test good formats
4079+
Response response = UtilIT.getFileCitationFormat(fileId,"EndNote", apiToken);
4080+
response.then().assertThat()
4081+
.statusCode(OK.getStatusCode());
4082+
assertTrue(response.prettyPrint().contains("<custom1>coffeeshop.png</custom1>"));
4083+
4084+
response = UtilIT.getFileCitationFormat(fileId,"RIS", apiToken);
4085+
response.then().assertThat()
4086+
.statusCode(OK.getStatusCode());
4087+
assertTrue(response.prettyPrint().contains("C1 - coffeeshop.png"));
4088+
4089+
response = UtilIT.getFileCitationFormat(fileId,"BibTeX", apiToken);
4090+
response.then().assertThat()
4091+
.statusCode(OK.getStatusCode());
4092+
assertTrue(response.prettyPrint().contains("author = {Finch, Fiona},"));
4093+
4094+
response = UtilIT.getFileCitationFormat(fileId,"CSL", apiToken);
4095+
response.then().assertThat()
4096+
.statusCode(OK.getStatusCode());
4097+
assertTrue(response.prettyPrint().contains("\"title\": \"Darwin's Finches\","));
4098+
4099+
response = UtilIT.getFileCitationFormat(fileId,"Internal", apiToken);
4100+
response.then().assertThat()
4101+
.statusCode(OK.getStatusCode());
4102+
assertTrue(response.prettyPrint().contains("coffeeshop.png [fileName]"));
4103+
4104+
// Test an unknown format
4105+
response = UtilIT.getFileCitationFormat(fileId,"bad", apiToken);
4106+
response.prettyPrint();
4107+
response.then().assertThat()
4108+
.statusCode(BAD_REQUEST.getStatusCode())
4109+
.body("message", equalTo(BundleUtil.getStringFromBundle("datasets.api.citation.invalidFormat")));
4110+
// Test an NULL format
4111+
response = UtilIT.getFileCitationFormat(fileId,null, apiToken);
4112+
response.prettyPrint();
4113+
response.then().assertThat()
4114+
.statusCode(BAD_REQUEST.getStatusCode())
4115+
.body("message", equalTo(BundleUtil.getStringFromBundle("datasets.api.citation.invalidFormat")));
4116+
4117+
// Test a user that doesn't have permission to get the citation
4118+
Response createUser2 = UtilIT.createRandomUser();
4119+
createUser2.then().assertThat().statusCode(OK.getStatusCode());
4120+
String apiToken2 = UtilIT.getApiTokenFromResponse(createUser2);
4121+
response = UtilIT.getFileCitationFormat(fileId,"EndNote", apiToken2);
4122+
response.prettyPrint();
4123+
response.then().assertThat()
4124+
.statusCode(FORBIDDEN.getStatusCode());
4125+
4126+
// Test a guest user after publishing
4127+
response = UtilIT.publishDataverseViaNativeApi(dataverseAlias, apiToken);
4128+
response.then().assertThat()
4129+
.statusCode(OK.getStatusCode());
4130+
response = UtilIT.publishDatasetViaNativeApi(datasetId, "major", apiToken);
4131+
response.then().assertThat()
4132+
.statusCode(OK.getStatusCode());
4133+
response = UtilIT.getFileCitationFormat(fileId,"EndNote", null);
4134+
response.prettyPrint();
4135+
response.then().assertThat()
4136+
.statusCode(OK.getStatusCode());
4137+
}
4138+
40584139
// This test is disabled because it is only compatible with the containerized development environment and would cause the Jenkins job to fail.
40594140
@Test
40604141
@Disabled

src/test/java/edu/harvard/iq/dataverse/api/UtilIT.java

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1452,6 +1452,14 @@ static Response getFileData(String fileId, String apiToken, String datasetVersio
14521452
.get("/api/files/" + fileId + "/versions/" + datasetVersionId);
14531453
}
14541454

1455+
static Response getFileCitationFormat(String dataFileId, String format, String apiToken) {
1456+
RequestSpecification request = given();
1457+
if (apiToken != null) {
1458+
request.header(API_TOKEN_HTTP_HEADER, apiToken);
1459+
}
1460+
return request.get("/api/access/datafile/" + dataFileId + "/citation/" + format);
1461+
}
1462+
14551463
static Response getFileVersionDifferences(String fileId, String apiToken) {
14561464
return getFileVersionDifferences(fileId, apiToken, null, null);
14571465
}

0 commit comments

Comments
 (0)