Skip to content

Commit 682703c

Browse files
authored
fix(#39): file picking and clean up init logic (#99)
1 parent ec5e1e3 commit 682703c

File tree

13 files changed

+151
-41
lines changed

13 files changed

+151
-41
lines changed

example/public/sdk-code.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,9 +23,9 @@ function initializeAspera(supportMulti, httpGatewayUrl, forceHttpGateway) {
2323
* HTTP Gateway URL can be set to support fallback to a gateway.
2424
* You can also force it to not start the desktop app.
2525
*/
26-
asperaSdk.init({appId, supportMultipleUsers, httpGatewayUrl, forceHttpGateway}).then(() => {
26+
asperaSdk.init({appId, supportMultipleUsers, httpGatewayUrl, forceHttpGateway}).then(response => {
2727
// The SDK started. Transfers and file picker can now be used.
28-
alert('SDK started');
28+
alert(`SDK started\n\n${JSON.stringify(response, undefined, 2)}`)
2929
}).catch(error => {
3030
// The SDK could not start. The app may not be running.
3131
console.error('SDK could not start', error);

example/src/App/index.tsx

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import './App.scss';
22
import { init, launch, testConnection } from '@ibm-aspera/sdk';
33
import { Header, HeaderGlobalAction, HeaderGlobalBar, HeaderName, Theme, Tab, TabList, Tabs, Button } from '@carbon/react';
4-
import { LogoGithub, Notification, NotificationOff, Sdk } from '@carbon/icons-react';
4+
import { LogoGithub, Notification, NotificationOff, Sdk, Reset } from '@carbon/icons-react';
55
import { Route, Routes, useLocation, useNavigate } from 'react-router';
66
import { useEffect, useState } from 'react';
77
import hljs from 'highlight.js/lib/core';
@@ -83,6 +83,11 @@ export default function App() {
8383
setDisableAlert(newValue);
8484
}
8585

86+
const resetTestApp = (): void => {
87+
localStorage.clear();
88+
window.location.reload();
89+
}
90+
8691
const openPath = (path: string): void => {
8792
navigate(path);
8893
};
@@ -91,6 +96,9 @@ export default function App() {
9196
<Header aria-label="Carbon Tutorial">
9297
<HeaderName prefix="IBM Aspera">JavaScript SDK Test Application</HeaderName>
9398
<HeaderGlobalBar>
99+
<HeaderGlobalAction aria-label="Reset test app" tooltipAlignment="end" className="action-icons" onClick={resetTestApp}>
100+
<Reset size={20} />
101+
</HeaderGlobalAction>
94102
<HeaderGlobalAction aria-label={disableAlert ? 'Enable alerts (needs reload)' : 'Disable alerts'} tooltipAlignment="end" className="action-icons" onClick={toggleAlert}>
95103
{disableAlert ? <Notification size={20} /> : <NotificationOff size={20} />}
96104
</HeaderGlobalAction>

example/src/Views/Initialize.tsx

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,21 +4,27 @@ import { useEffect, useState } from 'react';
44
import hljs from 'highlight.js';
55

66
export default function Initialize() {
7-
const [gatewayServer, setGatewayServer] = useState('');
7+
const [gatewayServer, setGatewayServer] = useState(localStorage.getItem('ASPERA-SDK-GATEWAY') || '');
88

99
useEffect(() => {
1010
document.querySelector('.cds--snippet-container > pre > code')?.classList.add('language-javascript');
1111
hljs.highlightAll();
1212
}, []);
1313

14+
const setGatewayUrl = (value: string): void => {
15+
localStorage.setItem('ASPERA-SDK-GATEWAY', value);
16+
setGatewayServer(value);
17+
}
18+
1419
return (
1520
<div className="example-pages">
1621
<h2>Code example</h2>
1722
<CodeSnippet type="multi" feedback="Copied to clipboard" maxCollapsedNumberOfRows={25}>{window.initializeAspera.toString()}</CodeSnippet>
1823
<h2>Try it out</h2>
19-
<TextInput id="gateway-server-id" className="code-input" value={gatewayServer} onChange={value => setGatewayServer(value.target.value)} labelText="HTTP Gateway URL" helperText="The HTTP Gateway Server to start up with Desktop" />
2024
<Button onClick={() => window.initializeAspera(false, gatewayServer, false)}>Initialize SDK</Button>
2125
<Button onClick={() => window.initializeAspera(true, gatewayServer, false)}>Initialize SDK (multi user)</Button>
26+
<Button onClick={() => window.initializeAspera(true, gatewayServer, true)}>Initialize SDK (force gateway)</Button>
27+
<TextInput id="gateway-server-id" className="code-input" value={gatewayServer} onChange={value => setGatewayUrl(value.target.value)} labelText="HTTP Gateway URL" helperText="The HTTP Gateway Server to start up with Desktop" />
2228
</div>
2329
);
2430
};

example/vite.config.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ export default defineConfig({
1313
},
1414
plugins: [
1515
react(),
16-
basicSsl()
16+
// basicSsl()
1717
],
1818
assetsInclude: ['**/*.md'],
1919
server: {

src/app/core.ts

Lines changed: 45 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
11
import {messages} from '../constants/messages';
22
import {client} from '../helpers/client/client';
33
import {errorLog, generateErrorBody, generatePromiseObjects, isValidTransferSpec, randomUUID, throwError} from '../helpers/helpers';
4-
import { getApiCall } from '../http-gateway/core';
4+
import {getApiCall, handleHttpGatewayDrop, httpGatewaySelectFileDialog, httpGatewaySelectFolderDialog} from '../http-gateway/core';
5+
import {HttpGatewayInfo} from '../http-gateway/models';
56
import {asperaSdk} from '../index';
6-
import {AsperaSdkInfo, TransferResponse} from '../models/aspera-sdk.model';
7+
import {AsperaSdkInfo, AsperaSdkClientInfo, TransferResponse} from '../models/aspera-sdk.model';
78
import {CustomBrandingOptions, DataTransferResponse, AsperaSdkSpec, BrowserStyleFile, AsperaSdkTransfer, FileDialogOptions, FolderDialogOptions, InitOptions, ModifyTransferOptions, ResumeTransferOptions, SafariExtensionEvent, TransferSpec, WebsocketEvent} from '../models/models';
89

910
/**
@@ -14,21 +15,24 @@ import {CustomBrandingOptions, DataTransferResponse, AsperaSdkSpec, BrowserStyle
1415
*/
1516
export const testConnection = (): Promise<any> => {
1617
return client.request('get_info')
17-
.then((data: AsperaSdkInfo) => {
18-
asperaSdk.globals.AsperaSdkInfo = data;
18+
.then((data: AsperaSdkClientInfo) => {
19+
asperaSdk.globals.asperaSdkInfo = data;
1920
asperaSdk.globals.asperaAppVerified = true;
20-
return data;
21+
return asperaSdk.globals.sdkResponseData;
2122
});
2223
};
2324

2425
/**
25-
* Initialize drag and drop.
26+
* Initialize drag and drop. HTTP Gateway does not need to init.
27+
* Ignore if only HTTP Gateway
2628
* @param initCall - Indicate if called via init flow and should not reject
2729
*
2830
* @returns a promise that resolves if the initialization was successful or not
2931
*/
3032
export const initDragDrop = (initCall?: boolean): Promise<boolean> => {
31-
if (!asperaSdk.isReady) {
33+
if (asperaSdk.useHttpGateway) {
34+
return Promise.resolve(true);
35+
} else if (!asperaSdk.isReady) {
3236
return throwError(messages.serverNotVerified);
3337
}
3438

@@ -89,6 +93,7 @@ export const init = (options?: InitOptions): Promise<any> => {
8993
return asperaSdk.activityTracking.setup()
9094
.then(() => testConnection())
9195
.then(() => initDragDrop(true))
96+
.then(() => asperaSdk.globals.sdkResponseData)
9297
.catch(handleErrors);
9398
};
9499

@@ -105,16 +110,20 @@ export const init = (options?: InitOptions): Promise<any> => {
105110

106111
asperaSdk.globals.httpGatewayUrl = finalHttpGatewayUrl;
107112

108-
return getApiCall('INFO').then(() => {
113+
return getApiCall('INFO').then((response: HttpGatewayInfo) => {
114+
asperaSdk.globals.httpGatewayInfo = response;
109115
asperaSdk.globals.httpGatewayVerified = true;
110116

111117
if (options.forceHttpGateway) {
112-
return asperaSdk.activityTracking.setup()
113-
.then(() => initDragDrop(true))
114-
.catch(handleErrors);
118+
return Promise.resolve(asperaSdk.globals.sdkResponseData);
115119
} else {
116120
return getDesktopStartCalls();
117121
}
122+
}).catch(error => {
123+
// If HTTP Gateway fails log and move on to desktop
124+
errorLog(messages.httpInitFail, error);
125+
126+
return getDesktopStartCalls();
118127
});
119128
}
120129

@@ -338,7 +347,9 @@ export const resumeTransfer = (id: string, options?: ResumeTransferOptions): Pro
338347
* @returns a promise that resolves with the selected file(s) and rejects if user cancels dialog
339348
*/
340349
export const showSelectFileDialog = (options?: FileDialogOptions): Promise<DataTransferResponse> => {
341-
if (!asperaSdk.isReady) {
350+
if (asperaSdk.useHttpGateway) {
351+
return httpGatewaySelectFileDialog(options);
352+
} else if (!asperaSdk.isReady) {
342353
return throwError(messages.serverNotVerified);
343354
}
344355

@@ -367,7 +378,9 @@ export const showSelectFileDialog = (options?: FileDialogOptions): Promise<DataT
367378
* @returns a promise that resolves with the selected folder(s) and rejects if user cancels dialog
368379
*/
369380
export const showSelectFolderDialog = (options?: FolderDialogOptions): Promise<DataTransferResponse> => {
370-
if (!asperaSdk.isReady) {
381+
if (asperaSdk.useHttpGateway) {
382+
return httpGatewaySelectFolderDialog(options);
383+
} else if (!asperaSdk.isReady) {
371384
return throwError(messages.serverNotVerified);
372385
}
373386

@@ -567,7 +580,7 @@ export const setBranding = (id: string, options: CustomBrandingOptions): Promise
567580
* @param elementSelector the selector of the element on the page that should watch for drop events
568581
*/
569582
export const createDropzone = (
570-
callback: (data: {event: any; files: DataTransferResponse}) => void,
583+
callback: (data: {event: DragEvent; files: DataTransferResponse}) => void,
571584
elementSelector: string,
572585
): void => {
573586
const elements = document.querySelectorAll(elementSelector);
@@ -576,16 +589,19 @@ export const createDropzone = (
576589
return;
577590
}
578591

579-
const dragEvent = (event: any) => {
592+
const dragEvent = (event: DragEvent) => {
580593
event.preventDefault();
581594
};
582595

583-
const dropEvent = (event: any) => {
596+
const dropEvent = (event: DragEvent) => {
584597
event.preventDefault();
585-
const files: BrowserStyleFile[] = [];
586598
if (event.dataTransfer && event.dataTransfer.files && event.dataTransfer.files.length && event.dataTransfer.files[0]) {
599+
const files: BrowserStyleFile[] = [];
600+
const rawFiles: File[] = [];
601+
587602
for (let i = 0; i < event.dataTransfer.files.length; i++) {
588603
const file = event.dataTransfer.files[i];
604+
rawFiles.push(file);
589605
files.push({
590606
lastModified: file.lastModified,
591607
name: file.name,
@@ -599,11 +615,17 @@ export const createDropzone = (
599615
app_id: asperaSdk.globals.appId,
600616
};
601617

602-
client.request('dropped_files', payload)
603-
.then((data: any) => callback({event, files: data}))
604-
.catch(error => {
605-
errorLog(messages.unableToReadDropped, error);
606-
});
618+
handleHttpGatewayDrop(rawFiles);
619+
620+
if (asperaSdk.isReady) {
621+
client.request('dropped_files', payload)
622+
.then((data: any) => callback({event, files: data}))
623+
.catch(error => {
624+
errorLog(messages.unableToReadDropped, error);
625+
});
626+
} else {
627+
callback({event, files: {dataTransfer: {files}}});
628+
}
607629
}
608630
};
609631

@@ -646,6 +668,6 @@ export const getInfo = (): Promise<AsperaSdkInfo> => {
646668
}
647669

648670
return new Promise((resolve, _) => {
649-
resolve(asperaSdk.globals.AsperaSdkInfo);
671+
resolve(asperaSdk.globals.sdkResponseData);
650672
});
651673
};

src/constants/messages.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,4 +28,5 @@ export const messages = {
2828
websocketClosedUnexpect: 'The websocket was closed unexpectedly',
2929
websocketNotReady: 'The websocket is not ready. Run init first',
3030
httpNotAvailable: 'IBM Aspera HTTP Gateway is not available',
31+
httpInitFail: 'IBM Aspera HTTP Gateway could not be started',
3132
};

src/http-gateway/core.ts

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import {asperaSdk} from '../index';
2+
import {FileDialogOptions, DataTransferResponse} from '../models/models';
23
import {HttpGatewayDownload, HttpGatewayDownloadLegacy, HttpGatewayInfo, HttpGatewayPresign, HttpGatewayUpload} from './models';
34

45
/**
@@ -45,3 +46,35 @@ export const getApiCall = (type: 'INFO'|'DOWNLOAD'|'UPLOAD'|'PRESIGN', body?: Bo
4546
return response.body;
4647
});
4748
};
49+
50+
/**
51+
* Handle drop events and store files for HTTP Gateway
52+
* This works on top of desktop.
53+
*/
54+
export const handleHttpGatewayDrop = (files: File[]): void => {
55+
files.forEach(file => {
56+
asperaSdk.httpGatewaySelectedFiles.set(file.name, file);
57+
});
58+
};
59+
60+
/**
61+
* Open native browser file picker for files
62+
*
63+
* @param options - File picker options
64+
*
65+
* @returns Promise that resolves with info about the files picked
66+
*/
67+
export const httpGatewaySelectFileDialog = (options?: FileDialogOptions): Promise<DataTransferResponse> => {
68+
return Promise.reject('TODO: Select file. Need to create form since showOpenFilePicker has limited browser support');
69+
};
70+
71+
/**
72+
* Open native browser file picker for folders
73+
*
74+
* @param options - File picker options
75+
*
76+
* @returns Promise that resolves with info about the folders picked
77+
*/
78+
export const httpGatewaySelectFolderDialog = (options?: FileDialogOptions): Promise<DataTransferResponse> => {
79+
return Promise.reject('TODO: Select folder. Need to create form since showOpenFilePicker has limited browser support');
80+
};

src/http-gateway/download.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ import {messages} from '../constants/messages';
1515
* You may not need to import anything from this file.
1616
*/
1717
export const httpDownload = (transferSpec: TransferSpec): Promise<AsperaSdkTransfer> => {
18-
if (!asperaSdk.supportsHttpGateway) {
18+
if (!asperaSdk.httpGatewayIsReady) {
1919
return throwError(messages.serverNotVerified, {type: 'download'});
2020
}
2121

src/http-gateway/index.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import {httpDownload} from './download';
22
import {httpUpload} from './upload';
3+
import {getApiCall, handleHttpGatewayDrop, httpGatewaySelectFileDialog, httpGatewaySelectFolderDialog} from './core';
34

45
/**
56
* HTTP Gateway Exports
@@ -12,4 +13,8 @@ import {httpUpload} from './upload';
1213
export {
1314
httpUpload,
1415
httpDownload,
16+
getApiCall,
17+
handleHttpGatewayDrop,
18+
httpGatewaySelectFileDialog,
19+
httpGatewaySelectFolderDialog
1520
};

src/http-gateway/models.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
export interface HttpGatewayInfo {
22
version: string;
33
name: string;
4-
upload_endpoint: string;
5-
download_endpoint: string;
4+
upload_endpoint: string[];
5+
download_endpoint: string[];
66
endpoints: string[];
77
}
88

0 commit comments

Comments
 (0)