Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

995x - Signposting fixes #9955

Merged
7 changes: 7 additions & 0 deletions doc/release-notes/9955-Signposting-updates.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
This release fixes several issues (#9952, #9953, #9957) where the Signposting output did not match the Signposting specification. These changes introduce backward-incompatibility, but since Signposting support was added recently (in Dataverse 5.14 in PR #8981), we feel it's best to do this clean up and not support the old implementation that was not fully compliant with the spec.

To fix #9952, we surround the license info with `<` and `>`.

To fix #9953, we no longer wrap the response in a `{"status":"OK","data":{` JSON object. This has also been noted in the guides at https://dataverse-guide--9955.org.readthedocs.build/en/9955/api/native-api.html#retrieve-signposting-information

To fix #9957, we corrected the mime/content type, changing it from `json+ld` to `ld+json`. For backward compatibility, we are still supporting the old one, for now.
4 changes: 2 additions & 2 deletions doc/sphinx-guides/source/api/native-api.rst
Original file line number Diff line number Diff line change
Expand Up @@ -2202,11 +2202,11 @@ Signposting involves the addition of a `Link <https://tools.ietf.org/html/rfc598

Here is an example of a "Link" header:

``Link: <https://doi.org/10.5072/FK2/YD5QDG>;rel="cite-as", <https://doi.org/10.5072/FK2/YD5QDG>;rel="describedby";type="application/vnd.citationstyles.csl+json",<https://demo.dataverse.org/api/datasets/export?exporter=schema.org&persistentId=doi:10.5072/FK2/YD5QDG>;rel="describedby";type="application/json+ld", <https://schema.org/AboutPage>;rel="type",<https://schema.org/Dataset>;rel="type", https://demo.dataverse.org/api/datasets/:persistentId/versions/1.0/customlicense?persistentId=doi:10.5072/FK2/YD5QDG;rel="license", <https://demo.dataverse.org/api/datasets/:persistentId/versions/1.0/linkset?persistentId=doi:10.5072/FK2/YD5QDG> ; rel="linkset";type="application/linkset+json"``
``Link: <https://doi.org/10.5072/FK2/YD5QDG>;rel="cite-as", <https://doi.org/10.5072/FK2/YD5QDG>;rel="describedby";type="application/vnd.citationstyles.csl+json",<https://demo.dataverse.org/api/datasets/export?exporter=schema.org&persistentId=doi:10.5072/FK2/YD5QDG>;rel="describedby";type="application/ld+json", <https://schema.org/AboutPage>;rel="type",<https://schema.org/Dataset>;rel="type", <https://demo.dataverse.org/api/datasets/:persistentId/versions/1.0/customlicense?persistentId=doi:10.5072/FK2/YD5QDG>;rel="license", <https://demo.dataverse.org/api/datasets/:persistentId/versions/1.0/linkset?persistentId=doi:10.5072/FK2/YD5QDG> ; rel="linkset";type="application/linkset+json"``

The URL for linkset information is discoverable under the ``rel="linkset";type="application/linkset+json`` entry in the "Link" header, such as in the example above.

The reponse includes a JSON object conforming to the `Signposting <https://signposting.org>`__ specification.
The reponse includes a JSON object conforming to the `Signposting <https://signposting.org>`__ specification. As part of this conformance, unlike most Dataverse API responses, the output is not wrapped in a ``{"status":"OK","data":{`` object.
Signposting is not supported for draft dataset versions.

.. code-block:: bash
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -500,7 +500,8 @@ public void process(HttpResponse response, HttpContext context) throws HttpExcep
.setRetryHandler(new DefaultHttpRequestRetryHandler(3, false))
.build()) {
HttpGet httpGet = new HttpGet(retrievalUri);
httpGet.addHeader("Accept", "application/json+ld, application/json");
//application/json+ld is for backward compatibility
httpGet.addHeader("Accept", "application/ld+json, application/json+ld, application/json");

HttpResponse response = httpClient.execute(httpGet);
String data = EntityUtils.toString(response.getEntity(), StandardCharsets.UTF_8);
Expand Down
18 changes: 13 additions & 5 deletions src/main/java/edu/harvard/iq/dataverse/DatasetPage.java
Original file line number Diff line number Diff line change
Expand Up @@ -2880,6 +2880,12 @@ public void sort() {
public String refresh() {
logger.fine("refreshing");

//In v5.14, versionId was null here. In 6.0, it appears not to be.
//This check is to handle the null if it reappears/occurs under other circumstances
if(versionId==null) {
logger.warning("versionId was null in refresh");
versionId = workingVersion.getId();
}
//dataset = datasetService.find(dataset.getId());
dataset = null;
workingVersion = null;
Expand All @@ -2889,10 +2895,9 @@ public String refresh() {
DatasetVersionServiceBean.RetrieveDatasetVersionResponse retrieveDatasetVersionResponse = null;

if (versionId != null) {
// versionId must have been set by now, in the init() method,
// regardless of how the page was originally called - by the dataset
// database id, by the persistent identifier, or by the db id of
// the version.
// versionId must have been set by now (see null check above), in the init()
// method, regardless of how the page was originally called - by the dataset
// database id, by the persistent identifier, or by the db id of the version.
this.workingVersion = datasetVersionService.findDeep(versionId);
dataset = workingVersion.getDataset();
}
Expand Down Expand Up @@ -6266,7 +6271,10 @@ public String getWebloaderUrlForDataset(Dataset d) {
String signpostingLinkHeader = null;

public String getSignpostingLinkHeader() {
if (!workingVersion.isReleased()) {
if ((workingVersion==null) || (!workingVersion.isReleased())) {
qqmyers marked this conversation as resolved.
Show resolved Hide resolved
if(workingVersion==null) {
logger.warning("workingVersion was null in getSignpostingLinkHeader");
}
return null;
}
if (signpostingLinkHeader == null) {
Expand Down
34 changes: 18 additions & 16 deletions src/main/java/edu/harvard/iq/dataverse/api/Datasets.java
Original file line number Diff line number Diff line change
Expand Up @@ -272,7 +272,7 @@ public Response getDataset(@Context ContainerRequestContext crc, @PathParam("id"

@GET
@Path("/export")
@Produces({"application/xml", "application/json", "application/html" })
@Produces({"application/xml", "application/json", "application/html", "application/ld+json" })
public Response exportDataset(@QueryParam("persistentId") String persistentId, @QueryParam("exporter") String exporter, @Context UriInfo uriInfo, @Context HttpHeaders headers, @Context HttpServletResponse response) {

try {
Expand Down Expand Up @@ -582,24 +582,26 @@ public Response getVersionMetadataBlock(@Context ContainerRequestContext crc,
@GET
@AuthRequired
@Path("{id}/versions/{versionId}/linkset")
public Response getLinkset(@Context ContainerRequestContext crc, @PathParam("id") String datasetId, @PathParam("versionId") String versionId, @Context UriInfo uriInfo, @Context HttpHeaders headers) {
if ( ":draft".equals(versionId) ) {
public Response getLinkset(@Context ContainerRequestContext crc, @PathParam("id") String datasetId, @PathParam("versionId") String versionId,
@Context UriInfo uriInfo, @Context HttpHeaders headers) {
if (":draft".equals(versionId)) {
return badRequest("Signposting is not supported on the :draft version");
}
User user = getRequestUser(crc);
return response(req -> {
DataverseRequest req = createDataverseRequest(getRequestUser(crc));
try {
DatasetVersion dsv = getDatasetVersionOrDie(req, versionId, findDatasetOrDie(datasetId), uriInfo, headers);
return ok(Json.createObjectBuilder().add(
"linkset",
new SignpostingResources(
systemConfig,
dsv,
JvmSettings.SIGNPOSTING_LEVEL1_AUTHOR_LIMIT.lookupOptional().orElse(""),
JvmSettings.SIGNPOSTING_LEVEL1_ITEM_LIMIT.lookupOptional().orElse("")
).getJsonLinkset()
)
);
}, user);
return Response
.ok(Json.createObjectBuilder()
.add("linkset",
new SignpostingResources(systemConfig, dsv,
JvmSettings.SIGNPOSTING_LEVEL1_AUTHOR_LIMIT.lookupOptional().orElse(""),
JvmSettings.SIGNPOSTING_LEVEL1_ITEM_LIMIT.lookupOptional().orElse(""))
.getJsonLinkset())
.build())
.type(MediaType.APPLICATION_JSON).build();
} catch (WrappedResponse wr) {
return wr.getResponse();
}
}

@GET
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -71,14 +71,14 @@ public String getLinks() {

String describedby = "<" + ds.getGlobalId().asURL().toString() + ">;rel=\"describedby\"" + ";type=\"" + "application/vnd.citationstyles.csl+json\"";
describedby += ",<" + systemConfig.getDataverseSiteUrl() + "/api/datasets/export?exporter=schema.org&persistentId="
+ ds.getProtocol() + ":" + ds.getAuthority() + "/" + ds.getIdentifier() + ">;rel=\"describedby\"" + ";type=\"application/json+ld\"";
+ ds.getProtocol() + ":" + ds.getAuthority() + "/" + ds.getIdentifier() + ">;rel=\"describedby\"" + ";type=\"application/ld+json\"";
valueList.add(describedby);

String type = "<https://schema.org/AboutPage>;rel=\"type\"";
type = "<https://schema.org/AboutPage>;rel=\"type\",<" + defaultFileTypeValue + ">;rel=\"type\"";
valueList.add(type);

String licenseString = DatasetUtil.getLicenseURI(workingDatasetVersion) + ";rel=\"license\"";
String licenseString = "<" + DatasetUtil.getLicenseURI(workingDatasetVersion) + ">;rel=\"license\"";
valueList.add(licenseString);

String linkset = "<" + systemConfig.getDataverseSiteUrl() + "/api/datasets/:persistentId/versions/"
Expand Down Expand Up @@ -116,7 +116,7 @@ public JsonArrayBuilder getJsonLinkset() {
systemConfig.getDataverseSiteUrl() + "/api/datasets/export?exporter=schema.org&persistentId=" + ds.getProtocol() + ":" + ds.getAuthority() + "/" + ds.getIdentifier()
).add(
"type",
"application/json+ld"
"application/ld+json"
)
);
JsonArrayBuilder linksetJsonObj = Json.createArrayBuilder();
Expand Down
13 changes: 12 additions & 1 deletion src/test/java/edu/harvard/iq/dataverse/api/SignpostingIT.java
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,7 @@ public void testSignposting() {
assertTrue(linkHeader.contains(datasetPid));
assertTrue(linkHeader.contains("cite-as"));
assertTrue(linkHeader.contains("describedby"));
assertTrue(linkHeader.contains("<http://creativecommons.org/publicdomain/zero/1.0>;rel=\"license\""));

Pattern pattern = Pattern.compile("<([^<]*)> ; rel=\"linkset\";type=\"application\\/linkset\\+json\"");
Matcher matcher = pattern.matcher(linkHeader);
Expand All @@ -92,7 +93,7 @@ public void testSignposting() {

String responseString = linksetResponse.getBody().asString();

JsonObject data = JsonUtil.getJsonObject(responseString).getJsonObject("data");
JsonObject data = JsonUtil.getJsonObject(responseString);
JsonObject lso = data.getJsonArray("linkset").getJsonObject(0);
System.out.println("Linkset: " + lso.toString());

Expand All @@ -101,6 +102,16 @@ public void testSignposting() {
assertTrue(lso.getString("anchor").indexOf("/dataset.xhtml?persistentId=" + datasetPid) > 0);
assertTrue(lso.containsKey("describedby"));

// Test export URL from link header
// regex inspired by https://stackoverflow.com/questions/68860255/how-to-match-the-closest-opening-and-closing-brackets
Pattern exporterPattern = Pattern.compile("[<\\[][^()\\[\\]]*?exporter=schema.org[^()\\[\\]]*[>\\]]");
Matcher exporterMatcher = exporterPattern.matcher(linkHeader);
exporterMatcher.find();

Response exportDataset = UtilIT.exportDataset(datasetPid, "schema.org");
exportDataset.prettyPrint();
exportDataset.then().assertThat().statusCode(OK.getStatusCode());

}

}
Loading