Skip to content

Commit 153ec4c

Browse files
committed
Inconsistent behaviour of dataset delete API
1 parent 98f448e commit 153ec4c

7 files changed

Lines changed: 36 additions & 73 deletions

File tree

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
## Bug Fix
2+
When calling DELETE "/api/datasets/{id}", of a released dataset, as a superuser, the call will no longer be 'upgraded' to a dataset destroy action. The user will receive an 'unauthorized' response with the message to call the API with /destroy in order to delete the dataset.

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -248,7 +248,7 @@ public boolean canIssueUpdateDatasetCommand(DvObject dvo){
248248

249249
// DELETE DATASET
250250
public boolean canIssueDeleteDatasetCommand(DvObject dvo){
251-
return canIssueCommand(dvo, DeleteDatasetCommand.class);
251+
return canIssueCommand(dvo, DeleteDatasetVersionCommand.class);
252252
}
253253

254254
// PUBLISH DATASET

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

Lines changed: 9 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -297,40 +297,25 @@ public Response exportDataset(@Context ContainerRequestContext crc, @QueryParam(
297297
@AuthRequired
298298
@Path("{id}")
299299
public Response deleteDataset(@Context ContainerRequestContext crc, @PathParam("id") String id) {
300-
// Internally, "DeleteDatasetCommand" simply redirects to "DeleteDatasetVersionCommand"
301-
// (and there's a comment that says "TODO: remove this command")
302-
// do we need an exposed API call for it?
303-
// And DeleteDatasetVersionCommand further redirects to DestroyDatasetCommand,
304-
// if the dataset only has 1 version... In other words, the functionality
305-
// currently provided by this API is covered between the "deleteDraftVersion" and
306-
// "destroyDataset" API calls.
307-
// (The logic below follows the current implementation of the underlying
308-
// commands!)
309-
310300
User u = getRequestUser(crc);
311301
return response( req -> {
312302
Dataset doomed = findDatasetOrDie(id);
313303
DatasetVersion doomedVersion = doomed.getLatestVersion();
314-
boolean destroy = false;
315304

316-
if (doomed.getVersions().size() == 1) {
317-
if (doomed.isReleased() && (!(u instanceof AuthenticatedUser) || !u.isSuperuser())) {
318-
throw new WrappedResponse(error(Response.Status.UNAUTHORIZED, "Only superusers can delete published datasets"));
319-
}
320-
destroy = true;
321-
} else {
322-
if (!doomedVersion.isDraft()) {
323-
throw new WrappedResponse(error(Response.Status.UNAUTHORIZED, "This is a published dataset with multiple versions. This API can only delete the latest version if it is a DRAFT"));
305+
if (!doomedVersion.isDraft()) {
306+
String msg = "This API can only delete the latest version if it is a DRAFT.";
307+
if (u.isSuperuser()) {
308+
msg += " Please use '/destroy' to delete the published version";
324309
}
310+
throw new WrappedResponse(error(Response.Status.UNAUTHORIZED, msg));
325311
}
326312

327-
// Gather the locations of the physical files that will need to be
328-
// deleted once the destroy command execution has been finalized:
329-
Map<Long, String> deleteStorageLocations = fileService.getPhysicalFilesToDelete(doomedVersion, destroy);
313+
// Gather the locations of the physical files that will need to be deleted
314+
Map<Long, String> deleteStorageLocations = fileService.getPhysicalFilesToDelete(doomedVersion);
330315

331-
execCommand( new DeleteDatasetCommand(req, findDatasetOrDie(id)));
316+
execCommand(new DeleteDatasetVersionCommand(req, doomed));
332317

333-
// If we have gotten this far, the destroy command has succeeded,
318+
// If we have gotten this far, the delete command has succeeded,
334319
// so we can finalize it by permanently deleting the physical files:
335320
// (DataFileService will double-check that the datafiles no
336321
// longer exist in the database, before attempting to delete

src/main/java/edu/harvard/iq/dataverse/api/datadeposit/ContainerManagerImpl.java

Lines changed: 1 addition & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,6 @@
1212
import edu.harvard.iq.dataverse.authorization.users.AuthenticatedUser;
1313
import edu.harvard.iq.dataverse.engine.command.exception.CommandException;
1414
import edu.harvard.iq.dataverse.engine.command.exception.CommandExecutionException;
15-
import edu.harvard.iq.dataverse.engine.command.impl.DeleteDatasetCommand;
1615
import edu.harvard.iq.dataverse.engine.command.impl.DeleteDatasetVersionCommand;
1716
import edu.harvard.iq.dataverse.engine.command.impl.PublishDatasetCommand;
1817
import edu.harvard.iq.dataverse.engine.command.impl.PublishDataverseCommand;
@@ -214,12 +213,6 @@ public void deleteContainer(String uri, AuthCredentials authCredentials, SwordCo
214213
Dataset dataset = dataset = datasetService.findByGlobalId(globalId);
215214
if (dataset != null) {
216215
Dataverse dvThatOwnsDataset = dataset.getOwner();
217-
/**
218-
* We are checking if DeleteDatasetVersionCommand can be
219-
* called even though DeleteDatasetCommand can be called
220-
* when a dataset hasn't been published. They should be
221-
* equivalent in terms of a permission check.
222-
*/
223216
DeleteDatasetVersionCommand deleteDatasetVersionCommand = new DeleteDatasetVersionCommand(dvRequest, dataset);
224217
if (!permissionService.isUserAllowedOn(user, deleteDatasetVersionCommand, dataset)) {
225218
throw new SwordError(UriRegistry.ERROR_BAD_REQUEST, "User " + user.getDisplayInfo().getTitle() + " is not authorized to modify " + dvThatOwnsDataset.getAlias());
@@ -253,7 +246,7 @@ public void deleteContainer(String uri, AuthCredentials authCredentials, SwordCo
253246
// dataset has never been published, this is just a sanity check (should always be draft)
254247
if (datasetVersionState.equals(DatasetVersion.VersionState.DRAFT)) {
255248
try {
256-
engineSvc.submit(new DeleteDatasetCommand(dvRequest, dataset));
249+
engineSvc.submit(new DeleteDatasetVersionCommand(dvRequest, dataset));
257250
logger.fine("dataset deleted");
258251
} catch (CommandExecutionException ex) {
259252
// internal error

src/main/java/edu/harvard/iq/dataverse/engine/command/impl/DeleteDatasetCommand.java

Lines changed: 0 additions & 33 deletions
This file was deleted.

src/main/java/edu/harvard/iq/dataverse/engine/command/impl/DestroyDatasetCommand.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -41,12 +41,12 @@
4141
import org.apache.solr.client.solrj.SolrServerException;
4242

4343
/**
44-
* Same as {@link DeleteDatasetCommand}, but does not stop if the dataset is
44+
* Same as {@link DeleteDatasetVersionCommand}, but does not stop if the dataset is
4545
* published. This command is reserved for super-users, if at all.
4646
*
4747
* @author michael
4848
*/
49-
// Since this is used by DeleteDatasetCommand, must have at least that permission
49+
// Since this is used by DeleteDatasetVersionCommand, must have at least that permission
5050
// (for released, user is checked for superuser)
5151
@RequiredPermissions( Permission.DeleteDatasetDraft )
5252
public class DestroyDatasetCommand extends AbstractVoidCommand {

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

Lines changed: 21 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -260,6 +260,10 @@ private void testDatasetSchemaValidationHelper(String dataverseAlias, String api
260260

261261
@Test
262262
public void testCreateDataset() {
263+
Response createSuperUser = UtilIT.createRandomUser();
264+
String superusername = UtilIT.getUsernameFromResponse(createSuperUser);
265+
UtilIT.setSuperuserStatus(superusername, true);
266+
String superuserApiToken = UtilIT.getApiTokenFromResponse(createSuperUser);
263267

264268
Response createUser = UtilIT.createRandomUser();
265269
createUser.prettyPrint();
@@ -339,19 +343,31 @@ public void testCreateDataset() {
339343
datasetAsJson.then().assertThat()
340344
.statusCode(OK.getStatusCode());
341345

342-
// OK, let's delete this dataset as well, and then delete the dataverse...
343-
344-
deleteDatasetResponse = UtilIT.deleteDatasetViaNativeApi(datasetId, apiToken);
346+
// Now publish the dataset and try to delete it (as superuser)
347+
Response publishResponse = UtilIT.publishDatasetViaNativeApi(datasetId, "major", apiToken);
348+
assertEquals(200, publishResponse.getStatusCode());
349+
deleteDatasetResponse = UtilIT.deleteDatasetViaNativeApi(datasetId, superuserApiToken);
350+
deleteDatasetResponse.prettyPrint();
351+
deleteDatasetResponse.then().assertThat()
352+
.body("message", containsString("/destroy"))
353+
.statusCode(UNAUTHORIZED.getStatusCode());
354+
// Try /destroy to get rid of the dataset
355+
deleteDatasetResponse = UtilIT.destroyDataset(datasetId, superuserApiToken);
345356
deleteDatasetResponse.prettyPrint();
346357
assertEquals(200, deleteDatasetResponse.getStatusCode());
347-
358+
359+
// Delete the dataverse
348360
Response deleteDataverseResponse = UtilIT.deleteDataverse(dataverseAlias, apiToken);
349361
deleteDataverseResponse.prettyPrint();
350362
assertEquals(200, deleteDataverseResponse.getStatusCode());
351363

364+
// Delete the Users
352365
Response deleteUserResponse = UtilIT.deleteUser(username);
353366
deleteUserResponse.prettyPrint();
354367
assertEquals(200, deleteUserResponse.getStatusCode());
368+
deleteUserResponse = UtilIT.deleteUser(superusername);
369+
deleteUserResponse.prettyPrint();
370+
assertEquals(200, deleteUserResponse.getStatusCode());
355371

356372
}
357373

@@ -3785,7 +3801,7 @@ public void testSemanticMetadataAPIs() {
37853801
deleteDraftResponse.then().assertThat().statusCode(OK.getStatusCode());
37863802

37873803
//Delete the published dataset
3788-
Response deletePublishedResponse = UtilIT.deleteDatasetViaNativeApi(datasetId, apiToken);
3804+
Response deletePublishedResponse = UtilIT.destroyDataset(datasetId, apiToken);
37893805
deletePublishedResponse.prettyPrint();
37903806
deleteDraftResponse.then().assertThat().statusCode(OK.getStatusCode());
37913807

0 commit comments

Comments
 (0)