Skip to content

Commit

Permalink
add API to delete DOIs when possible #5093
Browse files Browse the repository at this point in the history
  • Loading branch information
pdurbin committed May 27, 2020
1 parent 28f3222 commit 8aacc92
Show file tree
Hide file tree
Showing 6 changed files with 150 additions and 9 deletions.
15 changes: 15 additions & 0 deletions src/main/java/edu/harvard/iq/dataverse/api/Pids.java
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import edu.harvard.iq.dataverse.Dataset;
import static edu.harvard.iq.dataverse.api.AbstractApiBean.error;
import edu.harvard.iq.dataverse.authorization.users.User;
import edu.harvard.iq.dataverse.engine.command.impl.DeletePidCommand;
import edu.harvard.iq.dataverse.engine.command.impl.ReservePidCommand;
import edu.harvard.iq.dataverse.pidproviders.PidUtil;
import edu.harvard.iq.dataverse.util.BundleUtil;
Expand All @@ -11,6 +12,7 @@
import javax.json.JsonArray;
import javax.json.JsonArrayBuilder;
import javax.json.JsonObjectBuilder;
import javax.ws.rs.DELETE;
import javax.ws.rs.GET;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
Expand Down Expand Up @@ -94,4 +96,17 @@ public Response reservePid(@PathParam("id") String idSupplied) {
}
}

@DELETE
@Produces(MediaType.APPLICATION_JSON)
@Path("{id}/delete")
public Response deletePid(@PathParam("id") String idSupplied) {
try {
Dataset dataset = findDatasetOrDie(idSupplied);
execCommand(new DeletePidCommand(createDataverseRequest(findUserOrDie()), dataset));
return ok("PID deleted for " + dataset.getGlobalId().asString());
} catch (WrappedResponse ex) {
return ex.getResponse();
}
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
package edu.harvard.iq.dataverse.engine.command.impl;

import edu.harvard.iq.dataverse.Dataset;
import edu.harvard.iq.dataverse.GlobalIdServiceBean;
import edu.harvard.iq.dataverse.authorization.Permission;
import edu.harvard.iq.dataverse.authorization.users.AuthenticatedUser;
import edu.harvard.iq.dataverse.engine.command.AbstractVoidCommand;
import edu.harvard.iq.dataverse.engine.command.CommandContext;
import edu.harvard.iq.dataverse.engine.command.DataverseRequest;
import edu.harvard.iq.dataverse.engine.command.RequiredPermissions;
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.pidproviders.PidUtil;
import edu.harvard.iq.dataverse.settings.SettingsServiceBean;
import edu.harvard.iq.dataverse.util.BundleUtil;
import java.io.IOException;
import java.util.Collections;
import java.util.logging.Logger;

/**
* No required permissions because we check for superuser status.
*/
@RequiredPermissions({})
public class DeletePidCommand extends AbstractVoidCommand {

private static final Logger logger = Logger.getLogger(ReservePidCommand.class.getCanonicalName());

private final Dataset dataset;

public DeletePidCommand(DataverseRequest request, Dataset dataset) {
super(request, dataset);
this.dataset = dataset;
}

@Override
protected void executeImpl(CommandContext ctxt) throws CommandException {

if (!(getUser() instanceof AuthenticatedUser) || !getUser().isSuperuser()) {
throw new PermissionException(BundleUtil.getStringFromBundle("admin.api.auth.mustBeSuperUser"),
this, Collections.singleton(Permission.EditDataset), dataset);
}

String nonNullDefaultIfKeyNotFound = "";
String protocol = ctxt.settings().getValueForKey(SettingsServiceBean.Key.Protocol, nonNullDefaultIfKeyNotFound);
GlobalIdServiceBean idServiceBean = GlobalIdServiceBean.getBean(protocol, ctxt);
try {
// idServiceBean.deleteIdentifier(dataset); // didn't work
String baseUrl = System.getProperty("doi.baseurlstringnext");
String username = System.getProperty("doi.username");
String password = System.getProperty("doi.password");
int result = PidUtil.deleteDoi(dataset.getGlobalId().asString(), baseUrl, username, password);
if (result == 204) {
// Success! Clear the create time, etc.
dataset.setGlobalIdCreateTime(null);
dataset.setIdentifierRegistered(false);
ctxt.datasets().merge(dataset);
} else {
String message = "Unable to delete PID for dataset id " + dataset.getId() + ". Status code: " + result + ".";
throw new IllegalCommandException(message, this);
}
} catch (IOException ex) {
String message = "Problem deleting PID for dataset id " + dataset.getId() + ": " + ex.getLocalizedMessage();
logger.info(message);
throw new IllegalCommandException(message, this);
}
}

}
24 changes: 24 additions & 0 deletions src/main/java/edu/harvard/iq/dataverse/pidproviders/PidUtil.java
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,15 @@
import java.net.ProtocolException;
import java.net.URL;
import java.util.Base64;
import java.util.logging.Logger;
import javax.json.Json;
import javax.json.JsonObject;
import javax.json.JsonObjectBuilder;

public class PidUtil {

private static final Logger logger = Logger.getLogger(PidUtil.class.getCanonicalName());

public static JsonObjectBuilder queryDoi(String persistentId, String baseUrl, String username, String password) {
String doi = acceptOnlyDoi(persistentId);
URL url;
Expand Down Expand Up @@ -74,4 +77,25 @@ private static String acceptOnlyDoi(String persistentId) {
return globalId.getAuthority() + "/" + globalId.getIdentifier();
}

/**
* Deletes the DOI from DataCite if it can. Returns 204 if PID was deleted
* (only possible for "draft" DOIs), 405 (method not allowed) if the DOI
* wasn't deleted (because it's in "findable" state, for example, 404 if the
* DOI wasn't found, and possibly other status codes such as 500 if DataCite
* is down.
*/
public static int deleteDoi(String persistentId, String baseUrl, String username, String password) throws IOException {
String doi = acceptOnlyDoi(persistentId);
URL url = new URL(baseUrl + "/dois/" + doi);
HttpURLConnection connection = null;
connection = (HttpURLConnection) url.openConnection();
connection.setRequestMethod("DELETE");
String userpass = username + ":" + password;
String basicAuth = "Basic " + new String(Base64.getEncoder().encode(userpass.getBytes()));
connection.setRequestProperty("Authorization", basicAuth);
int status = connection.getResponseCode();
logger.fine("deleteDoi status for " + persistentId + ": " + status);
return status;
}

}
17 changes: 17 additions & 0 deletions src/test/java/edu/harvard/iq/dataverse/api/PidsIT.java
Original file line number Diff line number Diff line change
Expand Up @@ -91,4 +91,21 @@ public void testReservePid() {
*/
}

@Ignore
@Test
public void testDeletePid() {
String pid = "";
pid = "doi:10.70122/FK2/UA98UD";
Response createUser = UtilIT.createRandomUser();
createUser.prettyPrint();
createUser.then().assertThat()
.statusCode(OK.getStatusCode());
String username = UtilIT.getUsernameFromResponse(createUser);
String apiToken = UtilIT.getApiTokenFromResponse(createUser);

UtilIT.makeSuperUser(username).then().assertThat().statusCode(OK.getStatusCode());

Response deletePid = UtilIT.deletePid(pid, apiToken);
deletePid.prettyPrint();
}
}
7 changes: 7 additions & 0 deletions src/test/java/edu/harvard/iq/dataverse/api/UtilIT.java
Original file line number Diff line number Diff line change
Expand Up @@ -178,6 +178,13 @@ public static Response reservePid(String persistentId, String apiToken) {
return response;
}

public static Response deletePid(String persistentId, String apiToken) {
Response response = given()
.header(API_TOKEN_HTTP_HEADER, apiToken)
.delete("/api/pids/:persistentId/delete?persistentId=" + persistentId);
return response;
}

public static Response computeDataFileHashValue(String fileId, String alg, String apiToken) {
Response response = given()
.body(fileId)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,25 +6,34 @@
import org.junit.Test;
import org.junit.Ignore;

/**
* Useful for testing but requires DataCite credentials, etc.
*/
public class PidUtilTest {

/**
* Useful for testing but requires DataCite credentials, etc.
*/
@Ignore
@Test
public void testGetDoi() throws IOException {
String username = System.getenv("DataCiteUsername");
String password = System.getenv("DataCitePassword");
String baseUrl = "https://api.test.datacite.org";
String persistentId = "";
persistentId = "doi:10.70122/FK2/9BXT5O"; // findable
persistentId = "doi:10.70122/FK2/DOES-NOT-EXIST"; // does not exist
persistentId = "doi:10.70122/87W6-F672"; // draft
persistentId = "doi:10.70122/87W6-F672"; // draft
JsonObjectBuilder result = PidUtil.queryDoi(persistentId, baseUrl, username, password);
String pid = "";
pid = "doi:10.70122/QE5A-XN55";
JsonObjectBuilder result = PidUtil.queryDoi(pid, baseUrl, username, password);
String out = JsonUtil.prettyPrint(result.build());
System.out.println("out: " + out);
}

@Ignore
@Test
public void testDeleteDoi() throws IOException {
String username = System.getenv("DataCiteUsername");
String password = System.getenv("DataCitePassword");
String baseUrl = "https://api.test.datacite.org";
String pid = "";
pid = "doi:10.70122/FK2/UAESPI";
int result = PidUtil.deleteDoi(pid, baseUrl, username, password);
System.out.println("out: " + result);
}

}

0 comments on commit 8aacc92

Please sign in to comment.