From d2983744b4a565fd0630d71ab651f77cbd4a97f8 Mon Sep 17 00:00:00 2001 From: Nick Grosenbacher Date: Mon, 23 Sep 2024 10:08:21 -0400 Subject: [PATCH] SWC-6776 - Show ACL editor after successful upload --- .../client/events/UploadSuccessHandler.java | 6 ++- .../jsinterop/EntityAclEditorModalProps.java | 5 +- .../entity/download/UploadDialogWidget.java | 24 +++++++++- .../widget/entity/download/Uploader.java | 35 +++++++++++++- .../EntityAccessControlListModalWidget.java | 6 +++ ...ntityAccessControlListModalWidgetImpl.java | 12 ++++- .../entity/download/UploadDialogTest.java | 46 +++++++++++++++++-- .../widget/entity/download/UploaderTest.java | 21 +++++++-- 8 files changed, 141 insertions(+), 14 deletions(-) diff --git a/src/main/java/org/sagebionetworks/web/client/events/UploadSuccessHandler.java b/src/main/java/org/sagebionetworks/web/client/events/UploadSuccessHandler.java index 3108212f7b..cf3c6af051 100644 --- a/src/main/java/org/sagebionetworks/web/client/events/UploadSuccessHandler.java +++ b/src/main/java/org/sagebionetworks/web/client/events/UploadSuccessHandler.java @@ -1,5 +1,9 @@ package org.sagebionetworks.web.client.events; public interface UploadSuccessHandler { - void onSuccessfulUpload(); + /** + * Called when one or more files have been successfully uploaded. + * @param benefactorId the benefactor ID of all uploaded files. May be null if request to get benefactor fails + */ + void onSuccessfulUpload(String benefactorId); } diff --git a/src/main/java/org/sagebionetworks/web/client/jsinterop/EntityAclEditorModalProps.java b/src/main/java/org/sagebionetworks/web/client/jsinterop/EntityAclEditorModalProps.java index f51b48428d..8d067a33de 100644 --- a/src/main/java/org/sagebionetworks/web/client/jsinterop/EntityAclEditorModalProps.java +++ b/src/main/java/org/sagebionetworks/web/client/jsinterop/EntityAclEditorModalProps.java @@ -17,13 +17,15 @@ public interface Callback { public boolean open; public Callback onUpdateSuccess; public Callback onClose; + public boolean isAfterUpload; @JsOverlay public static EntityAclEditorModalProps create( String entityId, boolean open, Callback onUpdateSuccess, - Callback onClose + Callback onClose, + boolean isAfterUpload ) { EntityAclEditorModalProps props = new EntityAclEditorModalProps(); @@ -31,6 +33,7 @@ public static EntityAclEditorModalProps create( props.open = open; props.onUpdateSuccess = onUpdateSuccess; props.onClose = onClose; + props.isAfterUpload = isAfterUpload; return props; } } diff --git a/src/main/java/org/sagebionetworks/web/client/widget/entity/download/UploadDialogWidget.java b/src/main/java/org/sagebionetworks/web/client/widget/entity/download/UploadDialogWidget.java index fce5ca3c1b..af2608df27 100644 --- a/src/main/java/org/sagebionetworks/web/client/widget/entity/download/UploadDialogWidget.java +++ b/src/main/java/org/sagebionetworks/web/client/widget/entity/download/UploadDialogWidget.java @@ -1,21 +1,33 @@ package org.sagebionetworks.web.client.widget.entity.download; +import com.google.gwt.event.shared.EventBus; import com.google.gwt.user.client.ui.Widget; import com.google.inject.Inject; import org.sagebionetworks.repo.model.Entity; +import org.sagebionetworks.web.client.events.EntityUpdatedEvent; import org.sagebionetworks.web.client.utils.CallbackP; import org.sagebionetworks.web.client.widget.SynapseWidgetPresenter; +import org.sagebionetworks.web.client.widget.sharing.EntityAccessControlListModalWidget; public class UploadDialogWidget implements UploadDialogWidgetView.Presenter, SynapseWidgetPresenter { private UploadDialogWidgetView view; private Uploader uploader; + private final EventBus eventBus; + private final EntityAccessControlListModalWidget entityAclEditor; @Inject - public UploadDialogWidget(UploadDialogWidgetView view, Uploader uploader) { + public UploadDialogWidget( + UploadDialogWidgetView view, + Uploader uploader, + EventBus eventBus, + EntityAccessControlListModalWidget entityAccessControlListModalWidget + ) { this.view = view; this.uploader = uploader; + this.eventBus = eventBus; + this.entityAclEditor = entityAccessControlListModalWidget; view.setPresenter(this); } @@ -40,8 +52,16 @@ public void configure( view.configureDialog(title, body); // add handlers for closing the window - uploader.setSuccessHandler(() -> { + uploader.setSuccessHandler(benefactorId -> { view.hideDialog(); + if (benefactorId != null) { + entityAclEditor.configure( + benefactorId, + () -> eventBus.fireEvent(new EntityUpdatedEvent(benefactorId)), + true + ); + entityAclEditor.setOpen(true); + } }); uploader.setCancelHandler(() -> { diff --git a/src/main/java/org/sagebionetworks/web/client/widget/entity/download/Uploader.java b/src/main/java/org/sagebionetworks/web/client/widget/entity/download/Uploader.java index 7b46fcb42f..5132e89bb4 100644 --- a/src/main/java/org/sagebionetworks/web/client/widget/entity/download/Uploader.java +++ b/src/main/java/org/sagebionetworks/web/client/widget/entity/download/Uploader.java @@ -12,6 +12,7 @@ import com.google.gwt.user.client.ui.Widget; import com.google.inject.Inject; import java.util.List; +import org.sagebionetworks.repo.model.AccessControlList; import org.sagebionetworks.repo.model.Entity; import org.sagebionetworks.repo.model.Folder; import org.sagebionetworks.repo.model.attachment.UploadResult; @@ -790,7 +791,22 @@ public void onSuccess(Entity result) { entity = result; view.showInfo(DisplayConstants.TEXT_LINK_SUCCESS); if (successHandler != null) { - successHandler.onSuccessfulUpload(); + synapseClient.getEntityBenefactorAcl( + result.getId(), + new AsyncCallback() { + @Override + public void onSuccess(AccessControlList benefactorAcl) { + successHandler.onSuccessfulUpload(benefactorAcl.getId()); + } + + @Override + public void onFailure(Throwable caught) { + view.showErrorMessage(caught.getMessage()); + // Upload was still a success, benefactor ID is not required to continue + successHandler.onSuccessfulUpload(null); + } + } + ); } entityUpdated(); @@ -1009,7 +1025,22 @@ private void uploadSuccess() { view.resetToInitialState(); resetUploadProgress(); if (successHandler != null) { - successHandler.onSuccessfulUpload(); + synapseClient.getEntityBenefactorAcl( + parentEntityId, + new AsyncCallback() { + @Override + public void onSuccess(AccessControlList benefactorAcl) { + successHandler.onSuccessfulUpload(benefactorAcl.getId()); + } + + @Override + public void onFailure(Throwable caught) { + view.showErrorMessage(caught.getMessage()); + // Upload was still a success, benefactor ID is not required to continue. + successHandler.onSuccessfulUpload(null); + } + } + ); } entityUpdated(); } diff --git a/src/main/java/org/sagebionetworks/web/client/widget/sharing/EntityAccessControlListModalWidget.java b/src/main/java/org/sagebionetworks/web/client/widget/sharing/EntityAccessControlListModalWidget.java index 8ad84f0636..9208466432 100644 --- a/src/main/java/org/sagebionetworks/web/client/widget/sharing/EntityAccessControlListModalWidget.java +++ b/src/main/java/org/sagebionetworks/web/client/widget/sharing/EntityAccessControlListModalWidget.java @@ -12,5 +12,11 @@ void configure( EntityAclEditorModalProps.Callback onUpdateSuccess ); + void configure( + String entityId, + EntityAclEditorModalProps.Callback onUpdateSuccess, + boolean isAfterUpload + ); + void setOpen(boolean open); } diff --git a/src/main/java/org/sagebionetworks/web/client/widget/sharing/EntityAccessControlListModalWidgetImpl.java b/src/main/java/org/sagebionetworks/web/client/widget/sharing/EntityAccessControlListModalWidgetImpl.java index 02a241c5e5..384f37da82 100644 --- a/src/main/java/org/sagebionetworks/web/client/widget/sharing/EntityAccessControlListModalWidgetImpl.java +++ b/src/main/java/org/sagebionetworks/web/client/widget/sharing/EntityAccessControlListModalWidgetImpl.java @@ -31,13 +31,23 @@ public class EntityAccessControlListModalWidgetImpl public void configure( String entityId, EntityAclEditorModalProps.Callback onUpdateSuccess + ) { + configure(entityId, onUpdateSuccess, false); + } + + @Override + public void configure( + String entityId, + EntityAclEditorModalProps.Callback onUpdateSuccess, + boolean isAfterUpload ) { componentProps = EntityAclEditorModalProps.create( entityId, false, onUpdateSuccess, - () -> setOpen(false) + () -> setOpen(false), + isAfterUpload ); renderComponent(); } diff --git a/src/test/java/org/sagebionetworks/web/unitclient/widget/entity/download/UploadDialogTest.java b/src/test/java/org/sagebionetworks/web/unitclient/widget/entity/download/UploadDialogTest.java index a61643f87b..8aed2efd02 100644 --- a/src/test/java/org/sagebionetworks/web/unitclient/widget/entity/download/UploadDialogTest.java +++ b/src/test/java/org/sagebionetworks/web/unitclient/widget/entity/download/UploadDialogTest.java @@ -5,19 +5,23 @@ import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; -import com.google.gwt.user.client.ui.Widget; +import com.google.gwt.event.shared.EventBus; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; +import org.mockito.ArgumentCaptor; +import org.mockito.Captor; import org.mockito.Mock; import org.mockito.junit.MockitoJUnitRunner; import org.sagebionetworks.repo.model.Entity; import org.sagebionetworks.web.client.events.CancelHandler; import org.sagebionetworks.web.client.events.UploadSuccessHandler; +import org.sagebionetworks.web.client.jsinterop.EntityAclEditorModalProps; import org.sagebionetworks.web.client.utils.CallbackP; import org.sagebionetworks.web.client.widget.entity.download.UploadDialogWidget; import org.sagebionetworks.web.client.widget.entity.download.UploadDialogWidgetView; import org.sagebionetworks.web.client.widget.entity.download.Uploader; +import org.sagebionetworks.web.client.widget.sharing.EntityAccessControlListModalWidget; @RunWith(MockitoJUnitRunner.Silent.class) public class UploadDialogTest { @@ -28,11 +32,31 @@ public class UploadDialogTest { @Mock Uploader mockUploader; + @Mock + EventBus mockEventBus; + + @Mock + EntityAccessControlListModalWidget mockEntityAccessControlListModalWidget; + + @Captor + ArgumentCaptor uploadSuccessCaptor; + + @Captor + ArgumentCaptor< + EntityAclEditorModalProps.Callback + > updateAclSuccessCallbackCaptor; + UploadDialogWidget widget; @Before public void before() throws Exception { - widget = new UploadDialogWidget(view, mockUploader); + widget = + new UploadDialogWidget( + view, + mockUploader, + mockEventBus, + mockEntityAccessControlListModalWidget + ); } @Test @@ -54,8 +78,24 @@ public void testConfigure() { .configure(entity, parentEntityId, fileHandleIdCallback, isEntity); verify(view).configureDialog(eq(title), any()); - verify(mockUploader).setSuccessHandler(any(UploadSuccessHandler.class)); + verify(mockUploader).setSuccessHandler(uploadSuccessCaptor.capture()); verify(mockUploader).setCancelHandler(any(CancelHandler.class)); + + // simulate a successful upload + String benefactorId = "syn123"; + uploadSuccessCaptor.getValue().onSuccessfulUpload(benefactorId); + verify(view).hideDialog(); + verify(mockEntityAccessControlListModalWidget) + .configure( + eq(benefactorId), + updateAclSuccessCallbackCaptor.capture(), + eq(true) + ); + verify(mockEntityAccessControlListModalWidget).setOpen(true); + + // Simulate a successful ACL save + updateAclSuccessCallbackCaptor.getValue().run(); + verify(mockEventBus).fireEvent(any()); } @Test diff --git a/src/test/java/org/sagebionetworks/web/unitclient/widget/entity/download/UploaderTest.java b/src/test/java/org/sagebionetworks/web/unitclient/widget/entity/download/UploaderTest.java index 4fb03875e8..edf8ee069b 100644 --- a/src/test/java/org/sagebionetworks/web/unitclient/widget/entity/download/UploaderTest.java +++ b/src/test/java/org/sagebionetworks/web/unitclient/widget/entity/download/UploaderTest.java @@ -6,7 +6,6 @@ import static org.junit.Assert.assertTrue; import static org.mockito.Matchers.any; import static org.mockito.Matchers.anyInt; -import static org.mockito.Matchers.anyLong; import static org.mockito.Matchers.anyString; import static org.mockito.Matchers.eq; import static org.mockito.Mockito.doAnswer; @@ -30,13 +29,13 @@ import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.ArgumentCaptor; -import org.mockito.ArgumentMatchers; import org.mockito.Captor; import org.mockito.Mock; import org.mockito.Mockito; import org.mockito.invocation.InvocationOnMock; import org.mockito.junit.MockitoJUnitRunner; import org.mockito.stubbing.Answer; +import org.sagebionetworks.repo.model.AccessControlList; import org.sagebionetworks.repo.model.Entity; import org.sagebionetworks.repo.model.FileEntity; import org.sagebionetworks.repo.model.Folder; @@ -166,6 +165,7 @@ public class UploaderTest { private final Long defaultSynapseStorageId = 1L; public static final String SUCCESS_FILE_HANDLE = "99999"; + public static final String UPLOAD_BENEFACTOR_ID = "syn12345"; @Before public void before() throws Exception { @@ -291,6 +291,11 @@ public void testSetNewExternalPath() throws Exception { // this is the full success test // if entity is null, it should call synapseClient.createExternalFile() to create the FileEntity and // associate the path. + AsyncMockStubber + .callSuccessWith(new AccessControlList().setId(UPLOAD_BENEFACTOR_ID)) + .when(mockSynapseClient) + .getEntityBenefactorAcl(anyString(), any(AsyncCallback.class)); + uploader.setExternalFilePath( "http://fakepath.url/blah.xml", "", @@ -307,8 +312,10 @@ public void testSetNewExternalPath() throws Exception { eq(storageLocationId), any() ); + verify(mockSynapseClient) + .getEntityBenefactorAcl(anyString(), any(AsyncCallback.class)); verify(mockView).showInfo(anyString()); - verify(mockUploadSuccessHandler).onSuccessfulUpload(); + verify(mockUploadSuccessHandler).onSuccessfulUpload(UPLOAD_BENEFACTOR_ID); } @Test @@ -411,11 +418,17 @@ public void testDirectUploadHappyCase() throws Exception { .callSuccessWith(testEntity) .when(mockSynapseJavascriptClient) .getEntity(anyString(), any(OBJECT_TYPE.class), any(AsyncCallback.class)); + AsyncMockStubber + .callSuccessWith(new AccessControlList().setId(UPLOAD_BENEFACTOR_ID)) + .when(mockSynapseClient) + .getEntityBenefactorAcl(anyString(), any(AsyncCallback.class)); uploader.handleUploads(); verify(mockGlobalApplicationState).clearDropZoneHandler(); // SWC-5161 (cleared on handleUploads) verify(mockView).disableSelectionDuringUpload(); verify(mockSynapseClient) .setFileEntityFileHandle(any(), any(), any(), any()); + verify(mockSynapseClient) + .getEntityBenefactorAcl(anyString(), any(AsyncCallback.class)); verify(mockView).hideLoading(); assertEquals(UploadType.S3, uploader.getCurrentUploadType()); // verify upload success @@ -423,7 +436,7 @@ public void testDirectUploadHappyCase() throws Exception { verify(mockView).showSingleFileUploaded("entityID"); verify(mockView).clear(); verify(mockView, times(2)).resetToInitialState(); - verify(mockUploadSuccessHandler).onSuccessfulUpload(); + verify(mockUploadSuccessHandler).onSuccessfulUpload(UPLOAD_BENEFACTOR_ID); verify(mockEventBus).fireEvent(any(EntityUpdatedEvent.class)); }