Skip to content

Commit

Permalink
SWC-6734: Use legacy uploader for Google Cloud Platform bucket upload…
Browse files Browse the repository at this point in the history
…, and new SRC uploader for S3 (and S3-like) upload
  • Loading branch information
jay-hodgson committed Mar 20, 2024
1 parent 6f1b426 commit 0368b4c
Show file tree
Hide file tree
Showing 10 changed files with 1,244 additions and 523 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -257,6 +257,7 @@
import org.sagebionetworks.web.client.widget.upload.FileHandleLink;
import org.sagebionetworks.web.client.widget.upload.FileHandleUploadWidget;
import org.sagebionetworks.web.client.widget.upload.ImageUploadView;
import org.sagebionetworks.web.client.widget.upload.MultipartUploaderImpl;
import org.sagebionetworks.web.client.widget.user.UserBadge;
import org.sagebionetworks.web.client.widget.verification.VerificationSubmissionModalViewImpl;
import org.sagebionetworks.web.client.widget.verification.VerificationSubmissionRowViewImpl;
Expand Down Expand Up @@ -862,4 +863,6 @@ public interface PortalGinInjector extends Ginjector {
FollowingPagePresenter getFollowingPagePresenter();

ColumnModelsEditorWidget getColumnModelsEditorWidget();

MultipartUploaderImpl getLegacyMultipartUploader();
}
478 changes: 286 additions & 192 deletions src/main/java/org/sagebionetworks/web/client/PortalGinModule.java

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package org.sagebionetworks.web.client.jsinterop;

import jsinterop.annotations.JsConstructor;
import jsinterop.annotations.JsFunction;
import jsinterop.annotations.JsMethod;
import jsinterop.annotations.JsPackage;
import jsinterop.annotations.JsType;

@JsType(isNative = true, namespace = JsPackage.GLOBAL)
public class Promise<T> {

@JsFunction
public interface FunctionParam<T> {
void exec(T o);
}

@JsFunction
public interface ConstructorParam<T> {
void exec(FunctionParam<T> resolve, FunctionParam<T> reject);
}

@JsConstructor
public Promise(ConstructorParam parameters) {}

public native Promise<T> then(FunctionParam<T> f);

@JsMethod(name = "catch")
public native Promise<T> catch_(FunctionParam<Object> f);
}
130 changes: 108 additions & 22 deletions src/main/java/org/sagebionetworks/web/client/jsinterop/SRC.java
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
package org.sagebionetworks.web.client.jsinterop;

import com.google.gwt.core.client.JavaScriptObject;
import com.google.gwt.core.client.JsArrayString;
import jsinterop.annotations.JsFunction;
import jsinterop.annotations.JsNullable;
import jsinterop.annotations.JsPackage;
import jsinterop.annotations.JsType;
Expand All @@ -19,22 +21,32 @@ public static class SynapseComponents {
public static ReactComponentType<DatasetEditorProps> DatasetItemsEditor;
public static ReactComponentType<EntityFinderProps> EntityFinder;
public static ReactComponentType<EvaluationCardProps> EvaluationCard;
public static ReactComponentType<EvaluationEditorPageProps> EvaluationEditorPage;
public static ReactComponentType<
EvaluationEditorPageProps
> EvaluationEditorPage;
public static ReactComponentType<AccessTokenPageProps> AccessTokenPage;
public static ReactComponentType<DownloadCartPageProps> DownloadCartPage;
public static ReactComponentType<DownloadConfirmationProps> DownloadConfirmation;
public static ReactComponentType<
DownloadConfirmationProps
> DownloadConfirmation;
public static ReactComponentType<FullWidthAlertProps> FullWidthAlert;
public static ReactComponentType<OrientationBannerProps> OrientationBanner;
public static ReactComponentType<SchemaDrivenAnnotationEditorProps> SchemaDrivenAnnotationEditor;
public static ReactComponentType<
SchemaDrivenAnnotationEditorProps
> SchemaDrivenAnnotationEditor;
public static ReactComponentType<SynapseNavDrawerProps> SynapseNavDrawer;
public static ReactComponentType<EmptyProps> FavoritesPage;
public static ReactComponentType<EntityModalProps> EntityModal;
public static ReactComponentType<IconSvgProps> IconSvg;
public static ReactComponentType<EntityTypeIconProps> EntityTypeIcon;
public static ReactComponentType<UserProfileLinksProps> UserProfileLinks;
public static ReactComponentType<SkeletonButtonProps> SkeletonButton;
public static ReactComponentType<QueryWrapperPlotNavProps> QueryWrapperPlotNav;
public static ReactComponentType<StandaloneQueryWrapperProps> StandaloneQueryWrapper;
public static ReactComponentType<
QueryWrapperPlotNavProps
> QueryWrapperPlotNav;
public static ReactComponentType<
StandaloneQueryWrapperProps
> StandaloneQueryWrapper;
public static ReactComponentType<ForumSearchProps> ForumSearch;
public static ReactComponentType ReviewerDashboard;
public static ReactComponentType<ProvenanceGraphProps> ProvenanceGraph;
Expand All @@ -46,28 +58,60 @@ public static class SynapseComponents {
public static ReactComponentType<LoginPageProps> LoginPage;
public static ReactComponentType<HasAccessProps> HasAccess;
public static ReactComponentType<UserCardProps> UserCard;
public static ReactComponentType<AccountLevelBadgesProps> AccountLevelBadges;
public static ReactComponentType<
AccountLevelBadgesProps
> AccountLevelBadges;
public static ReactComponentType<PageProgressProps> PageProgress;
public static ReactComponentType<TermsAndConditionsProps> TermsAndConditions;
public static ReactComponentType<
TermsAndConditionsProps
> TermsAndConditions;
public static ReactComponentType<IDUReportProps> IDUReport;
public static ReactComponentType CertificationQuiz;
public static ReactComponentType<EntityPageBreadcrumbsProps> EntityPageBreadcrumbs;
public static ReactComponentType<EntityPageTitleBarProps> EntityPageTitleBar;
public static ReactComponentType<EntityActionMenuPropsJsInterop> EntityActionMenu;
public static ReactComponentType<
EntityPageBreadcrumbsProps
> EntityPageBreadcrumbs;
public static ReactComponentType<
EntityPageTitleBarProps
> EntityPageTitleBar;
public static ReactComponentType<
EntityActionMenuPropsJsInterop
> EntityActionMenu;
public static ReactComponentType<HtmlPreviewProps> HtmlPreview;
public static ReactComponentType<CreatedByModifiedByProps> CreatedByModifiedBy;
public static ReactComponentType<TwoFactorAuthSettingsPanelProps> TwoFactorAuthSettingsPanel;
public static ReactComponentType<TwoFactorBackupCodesProps> TwoFactorBackupCodes;
public static ReactComponentType<TwoFactorEnrollmentFormProps> TwoFactorEnrollmentForm;
public static ReactComponentType<
CreatedByModifiedByProps
> CreatedByModifiedBy;
public static ReactComponentType<
TwoFactorAuthSettingsPanelProps
> TwoFactorAuthSettingsPanel;
public static ReactComponentType<
TwoFactorBackupCodesProps
> TwoFactorBackupCodes;
public static ReactComponentType<
TwoFactorEnrollmentFormProps
> TwoFactorEnrollmentForm;
public static ReactComponentType<EmptyProps> SubscriptionPage;
public static ReactComponentType<AccessRequirementListProps> AccessRequirementList;
public static ReactComponentType<TableColumnSchemaEditorProps> TableColumnSchemaEditor;
public static ReactComponentType<
AccessRequirementListProps
> AccessRequirementList;
public static ReactComponentType<
TableColumnSchemaEditorProps
> TableColumnSchemaEditor;
public static ReactComponentType<EntityHeaderTableProps> EntityHeaderTable;
public static ReactComponentType<AvailableEvaluationQueueListProps> AvailableEvaluationQueueList;
public static ReactComponentType<AccessRequirementRelatedProjectsListProps> AccessRequirementRelatedProjectsList;
public static ReactComponentType<CreateTableViewWizardProps> CreateTableViewWizard;
public static ReactComponentType<SqlDefinedTableEditorModalProps> SqlDefinedTableEditorModal;
public static ReactComponentType<EntityViewScopeEditorModalProps> EntityViewScopeEditorModal;
public static ReactComponentType<
AvailableEvaluationQueueListProps
> AvailableEvaluationQueueList;
public static ReactComponentType<
AccessRequirementRelatedProjectsListProps
> AccessRequirementRelatedProjectsList;
public static ReactComponentType<
CreateTableViewWizardProps
> CreateTableViewWizard;
public static ReactComponentType<
SqlDefinedTableEditorModalProps
> SqlDefinedTableEditorModal;
public static ReactComponentType<
EntityViewScopeEditorModalProps
> EntityViewScopeEditorModal;

/**
* Pushes a global toast message. In SWC, you should use {@link DisplayUtils#notify}, rather than calling this method directly.
Expand All @@ -87,12 +131,54 @@ public static class SynapseContext {

/* We use FullContextProvider because it will provide the SynapseContext, react-query QueryContext, and MUI Theme
context for all React trees that we render */
public static ReactComponentType<SynapseReactClientFullContextProviderProps> FullContextProvider;
public static ReactComponentType<
SynapseReactClientFullContextProviderProps
> FullContextProvider;
}

@JsType(isNative = true)
public static class SynapseConstants {

public static JsArrayString PERSISTENT_LOCAL_STORAGE_KEYS;
}

@JsType(isNative = true)
public static class SynapseClient {

@JsType(isNative = true, namespace = JsPackage.GLOBAL, name = "Object")
public static class ProgressCallback {

public double value;
public double total;
}

@JsType(isNative = true, namespace = JsPackage.GLOBAL, name = "Object")
public static class FileUploadComplete {

public String fileHandleId;
public String fileName;
}

@FunctionalInterface
@JsFunction
public interface Progress {
void onProgress(ProgressCallback callback);
}

@FunctionalInterface
@JsFunction
public interface IsCancelled {
boolean isCancelled();
}

public static native Promise<FileUploadComplete> uploadFile(
String accessToken,
String filename,
JavaScriptObject file, // blob
int storageLocationId,
String contentType,
Progress progressCallback,
IsCancelled getIsCancelled
);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
import org.sagebionetworks.web.client.DisplayUtils;
import org.sagebionetworks.web.client.GWTWrapper;
import org.sagebionetworks.web.client.GlobalApplicationState;
import org.sagebionetworks.web.client.PortalGinInjector;
import org.sagebionetworks.web.client.SynapseClientAsync;
import org.sagebionetworks.web.client.SynapseJSNIUtils;
import org.sagebionetworks.web.client.SynapseJavascriptClient;
Expand Down Expand Up @@ -62,8 +63,8 @@ public class Uploader
SynapseWidgetPresenter,
ProgressingFileUploadHandler {

public static final long OLD_BROWSER_MAX_SIZE = (long) ClientProperties.MB *
5; // 5MB
public static final long OLD_BROWSER_MAX_SIZE =
(long) ClientProperties.MB * 5; // 5MB
private UploaderView view;
private UploadSuccessHandler successHandler;
private CancelHandler cancelHandler;
Expand All @@ -77,6 +78,7 @@ public class Uploader
private GlobalApplicationState globalAppState;
private GWTWrapper gwt;
MultipartUploader multiPartUploader;
MultipartUploader legacyMultiPartUploader;
AuthenticationController authenticationController;

private String[] fileNames;
Expand Down Expand Up @@ -107,7 +109,8 @@ public Uploader(
S3DirectUploader s3DirectUploader,
SynapseJavascriptClient jsClient,
SynapseProperties synapseProperties,
EventBus eventBus
EventBus eventBus,
PortalGinInjector ginInjector
) {
this.view = view;
this.synapseClient = synapseClient;
Expand All @@ -122,6 +125,7 @@ public Uploader(
this.jsClient = jsClient;
this.synapseProperties = synapseProperties;
this.eventBus = eventBus;
legacyMultiPartUploader = ginInjector.getLegacyMultipartUploader();
view.setPresenter(this);
}

Expand Down Expand Up @@ -184,7 +188,8 @@ public String[] getSelectedFileNames() {

public String getSelectedFilesText() {
String[] selectedFiles = getSelectedFileNames();
if (selectedFiles == null) return ""; else if (selectedFiles.length == 1) {
if (selectedFiles == null) return "";
else if (selectedFiles.length == 1) {
return selectedFiles[0];
} else {
return selectedFiles.length + " files";
Expand Down Expand Up @@ -257,17 +262,18 @@ public void onSuccess(List<UploadDestination> uploadDestinations) {
uploadDestinations.get(0) instanceof S3UploadDestination
) {
currentUploadType = UploadType.S3;
storageLocationId =
uploadDestinations.get(0).getStorageLocationId();
storageLocationId = uploadDestinations
.get(0)
.getStorageLocationId();
updateUploadBannerView(uploadDestinations.get(0));
} else if (
uploadDestinations.get(
0
) instanceof ExternalGoogleCloudUploadDestination
uploadDestinations.get(0) instanceof
ExternalGoogleCloudUploadDestination
) {
currentUploadType = UploadType.GOOGLECLOUDSTORAGE;
storageLocationId =
uploadDestinations.get(0).getStorageLocationId();
storageLocationId = uploadDestinations
.get(0)
.getStorageLocationId();
updateUploadBannerView(uploadDestinations.get(0));
} else if (
uploadDestinations.get(0) instanceof ExternalUploadDestination
Expand Down Expand Up @@ -301,9 +307,8 @@ public void onSuccess(List<UploadDestination> uploadDestinations) {
updateUploadBannerView(externalUploadDestination);
// direct to s3(-like) storage
} else if (
uploadDestinations.get(
0
) instanceof ExternalObjectStoreUploadDestination
uploadDestinations.get(0) instanceof
ExternalObjectStoreUploadDestination
) {
ExternalObjectStoreUploadDestination externalUploadDestination =
(ExternalObjectStoreUploadDestination) uploadDestinations.get(
Expand Down Expand Up @@ -669,14 +674,18 @@ public void directUploadStep2(String fileName) {
view
);
} else {
this.multiPartUploader.uploadFile(
fileName,
contentType,
currentFile,
this,
storageLocationId,
view
);
// TODO: PLFM-8252: If Google Cloud platform Synapse solution supports parallel upload, then remove legacyMultiPartUploader (and all associated code)
MultipartUploader currentUploader = currentUploadType == UploadType.S3
? multiPartUploader
: legacyMultiPartUploader;
currentUploader.uploadFile(
fileName,
contentType,
currentFile,
this,
storageLocationId,
view
);
}
}

Expand Down Expand Up @@ -1067,8 +1076,7 @@ public static double calculatePercentOverAllFiles(
) {
double percentPerFile = 1.0 / (double) numberFiles;
double percentOfAllFiles =
percentPerFile *
percentOfCurrentFile +
percentPerFile * percentOfCurrentFile +
(percentPerFile * currentFileIndex);
return percentOfAllFiles;
}
Expand Down
Loading

0 comments on commit 0368b4c

Please sign in to comment.