diff --git a/vospace-ui-backend/src/main/java/it/inaf/ia2/vospace/ui/controller/UploadController.java b/vospace-ui-backend/src/main/java/it/inaf/ia2/vospace/ui/controller/UploadController.java index e3fb16aa4754b8de23964b8b1ae8586f219e4e28..7ddce8eebf291b00147254beac5200bd46fbc5a3 100644 --- a/vospace-ui-backend/src/main/java/it/inaf/ia2/vospace/ui/controller/UploadController.java +++ b/vospace-ui-backend/src/main/java/it/inaf/ia2/vospace/ui/controller/UploadController.java @@ -99,13 +99,22 @@ public class UploadController extends BaseController { try { String uploadUrl = obtainUploadUrl(nodeUri); result.setUrl(uploadUrl); - } catch (VOSpaceException ex) { - result.setError(ex.getMessage()); } catch (Throwable t) { - LOG.error("Error while obtaining upload URL for " + nodeUri, t); - result.setError("Unable to obtain upload URL"); - } + if (t instanceof VOSpaceException) { + result.setError(t.getMessage()); + } else { + LOG.error("Error while obtaining upload URL for " + nodeUri, t); + result.setError("Unable to obtain upload URL"); + } + try { + // attempt to cleanup node metadata + client.deleteNode(path); + } catch (Throwable dt) { + LOG.error("Unable to remove node after failed upload URL retrieval", dt); + result.setError("Retrieval of upload URL failed. Manual cleanup of node metadata may be necessary"); + } + } return result; }, Runnable::run); // Passing current thread Executor to CompletableFuture to avoid "No thread-bound request found" exception } diff --git a/vospace-ui-backend/src/test/java/it/inaf/ia2/vospace/ui/controller/UploadControllerTest.java b/vospace-ui-backend/src/test/java/it/inaf/ia2/vospace/ui/controller/UploadControllerTest.java index 50e0459f2488dcceb27bff64e4ecb1ec3793a295..31de6d21f785fa8cd470a03c377b263a994f78fd 100644 --- a/vospace-ui-backend/src/test/java/it/inaf/ia2/vospace/ui/controller/UploadControllerTest.java +++ b/vospace-ui-backend/src/test/java/it/inaf/ia2/vospace/ui/controller/UploadControllerTest.java @@ -16,8 +16,11 @@ import static org.hamcrest.core.IsNull.nullValue; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.eq; import org.mockito.Mock; import static org.mockito.Mockito.doThrow; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; @@ -158,6 +161,8 @@ public class UploadControllerTest { .andExpect(status().isOk()) .andExpect(jsonPath("$[0].error").value("Unable to connect")) .andExpect(jsonPath("$[0].url").value(nullValue())); + + verify(client, times(1)).deleteNode(eq("/mynode/test.txt")); } @Test @@ -177,5 +182,29 @@ public class UploadControllerTest { .andExpect(status().isOk()) .andExpect(jsonPath("$[0].error").value("Unable to obtain upload URL")) .andExpect(jsonPath("$[0].url").value(nullValue())); + + verify(client, times(1)).deleteNode(eq("/mynode/test.txt")); + } + + @Test + public void testUploadUrlErrorWithNodeDeletionError() throws Exception { + + UploadFilesData data = new UploadFilesData(); + data.setParentPath("/mynode"); + data.setFiles(List.of("test.txt")); + + doThrow(new NullPointerException()).when(client).getFileServiceEndpoint(any()); + doThrow(new VOSpaceException("error")).when(client).deleteNode(any()); + + mockMvc.perform(post("/preupload") + .sessionAttr("user_data", user) + .contentType(MediaType.APPLICATION_JSON) + .content(MAPPER.writeValueAsString(data))) + .andDo(print()) + .andExpect(status().isOk()) + .andExpect(jsonPath("$[0].error").value("Retrieval of upload URL failed. Manual cleanup of node metadata may be necessary")) + .andExpect(jsonPath("$[0].url").value(nullValue())); + + verify(client, times(1)).deleteNode(eq("/mynode/test.txt")); } }