@ibm-aspera/sdk 0.2.12 → 0.2.29

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (111) hide show
  1. package/.editorconfig +13 -0
  2. package/.github/CODEOWNERS +1 -0
  3. package/.github/CODE_OF_CONDUCT.md +128 -0
  4. package/.github/CONTRIBUTING.md +147 -0
  5. package/.github/dependabot.yml +10 -0
  6. package/.github/workflows/ci.yml +39 -0
  7. package/.github/workflows/documentation.yml +44 -0
  8. package/.github/workflows/publish.yml +23 -0
  9. package/.github/workflows/version.yml +32 -0
  10. package/CHANGELOG.md +204 -0
  11. package/docs/DEVELOPMENT.md +38 -0
  12. package/eslint.config.js +104 -0
  13. package/example/README.md +7 -0
  14. package/example/index.html +14 -0
  15. package/example/package-lock.json +2989 -0
  16. package/example/package.json +30 -0
  17. package/example/public/404.html +5 -0
  18. package/example/public/sdk-code.js +326 -0
  19. package/example/src/App/App.scss +40 -0
  20. package/example/src/App/index.tsx +196 -0
  21. package/example/src/Views/AllTogether.tsx +26 -0
  22. package/example/src/Views/DragDrop.tsx +23 -0
  23. package/example/src/Views/Home.tsx +10 -0
  24. package/example/src/Views/Initialize.tsx +31 -0
  25. package/example/src/Views/Installer.tsx +154 -0
  26. package/example/src/Views/MonitorTransfers.tsx +88 -0
  27. package/example/src/Views/Other.tsx +24 -0
  28. package/example/src/Views/SelectItems.tsx +46 -0
  29. package/example/src/Views/StartTransfer.tsx +37 -0
  30. package/example/src/Views/Test.tsx +20 -0
  31. package/example/src/Views/Views.scss +111 -0
  32. package/example/src/helpers/index.ts +19 -0
  33. package/example/src/index.scss +47 -0
  34. package/example/src/main.tsx +17 -0
  35. package/example/src/vite-env.d.ts +2 -0
  36. package/example/tsconfig.json +30 -0
  37. package/example/vite.config.ts +23 -0
  38. package/jest.config.js +19 -0
  39. package/jest.setup.js +0 -0
  40. package/package.json +4 -4
  41. package/renovate.json +12 -0
  42. package/src/app/core.ts +765 -0
  43. package/src/app/installer.ts +53 -0
  44. package/src/connect/core.ts +83 -0
  45. package/src/constants/constants.ts +19 -0
  46. package/src/constants/messages.ts +35 -0
  47. package/src/helpers/client/client.ts +11 -0
  48. package/src/helpers/client/http-client.ts +92 -0
  49. package/src/helpers/client/safari-client.ts +334 -0
  50. package/src/helpers/helpers.ts +253 -0
  51. package/src/helpers/http.ts +39 -0
  52. package/src/helpers/ws.ts +191 -0
  53. package/src/http-gateway/core.ts +273 -0
  54. package/src/http-gateway/download.ts +217 -0
  55. package/src/http-gateway/index.ts +19 -0
  56. package/src/http-gateway/models.ts +20 -0
  57. package/src/http-gateway/upload.ts +148 -0
  58. package/src/index.ts +72 -0
  59. package/src/models/aspera-sdk.model.ts +446 -0
  60. package/src/models/models.ts +740 -0
  61. package/tests/client.spec.ts +52 -0
  62. package/tests/core.spec.ts +13 -0
  63. package/tests/helpers.spec.ts +127 -0
  64. package/tests/http.spec.ts +14 -0
  65. package/tests/installer.spec.ts +135 -0
  66. package/tests/mocks.ts +11 -0
  67. package/tsconfig.json +14 -0
  68. package/tsconfig.module.json +16 -0
  69. package/typedoc.js +7 -0
  70. package/webpack.config.js +35 -0
  71. package/dist/commonjs/app/core.d.ts +0 -191
  72. package/dist/commonjs/app/core.js +0 -682
  73. package/dist/commonjs/app/installer.d.ts +0 -9
  74. package/dist/commonjs/app/installer.js +0 -50
  75. package/dist/commonjs/connect/core.d.ts +0 -11
  76. package/dist/commonjs/connect/core.js +0 -73
  77. package/dist/commonjs/constants/constants.d.ts +0 -8
  78. package/dist/commonjs/constants/constants.js +0 -11
  79. package/dist/commonjs/constants/messages.d.ts +0 -35
  80. package/dist/commonjs/constants/messages.js +0 -38
  81. package/dist/commonjs/helpers/client/client.d.ts +0 -5
  82. package/dist/commonjs/helpers/client/client.js +0 -7
  83. package/dist/commonjs/helpers/client/http-client.d.ts +0 -42
  84. package/dist/commonjs/helpers/client/http-client.js +0 -84
  85. package/dist/commonjs/helpers/client/safari-client.d.ts +0 -101
  86. package/dist/commonjs/helpers/client/safari-client.js +0 -264
  87. package/dist/commonjs/helpers/helpers.d.ts +0 -109
  88. package/dist/commonjs/helpers/helpers.js +0 -249
  89. package/dist/commonjs/helpers/http.d.ts +0 -16
  90. package/dist/commonjs/helpers/http.js +0 -42
  91. package/dist/commonjs/helpers/ws.d.ts +0 -62
  92. package/dist/commonjs/helpers/ws.js +0 -176
  93. package/dist/commonjs/http-gateway/core.d.ts +0 -76
  94. package/dist/commonjs/http-gateway/core.js +0 -254
  95. package/dist/commonjs/http-gateway/download.d.ts +0 -14
  96. package/dist/commonjs/http-gateway/download.js +0 -186
  97. package/dist/commonjs/http-gateway/index.d.ts +0 -11
  98. package/dist/commonjs/http-gateway/index.js +0 -11
  99. package/dist/commonjs/http-gateway/models.d.ts +0 -16
  100. package/dist/commonjs/http-gateway/models.js +0 -2
  101. package/dist/commonjs/http-gateway/upload.d.ts +0 -14
  102. package/dist/commonjs/http-gateway/upload.js +0 -124
  103. package/dist/commonjs/index.d.ts +0 -8
  104. package/dist/commonjs/index.js +0 -100
  105. package/dist/commonjs/models/aspera-sdk.model.d.ts +0 -245
  106. package/dist/commonjs/models/aspera-sdk.model.js +0 -312
  107. package/dist/commonjs/models/models.d.ts +0 -712
  108. package/dist/commonjs/models/models.js +0 -2
  109. package/dist/js/aspera-sdk.js +0 -3
  110. package/dist/js/aspera-sdk.js.LICENSE.txt +0 -15
  111. package/dist/js/aspera-sdk.js.map +0 -1
@@ -0,0 +1,253 @@
1
+ import {baseInstallerUrl, installerUrl} from '../constants/constants';
2
+ import {ErrorResponse, InstallerUrlInfo, PromiseObject, TransferSpec} from '../models/models';
3
+
4
+ /**
5
+ * Generates promise object that can be resolved or rejected via functions
6
+ *
7
+ * @returns an object containing the promise, the resolver and rejecter
8
+ */
9
+ export const generatePromiseObjects = (): PromiseObject => {
10
+ let resolver: (response: any) => void;
11
+ let rejecter: (response: any) => void;
12
+ const promise = new Promise((resolve, reject) => {
13
+ resolver = resolve;
14
+ rejecter = reject;
15
+ });
16
+ return {
17
+ promise,
18
+ resolver,
19
+ rejecter
20
+ };
21
+ };
22
+
23
+ /**
24
+ * Log errors from Aspera SDK
25
+ *
26
+ * @param message the message indicating the error encountered
27
+ * @param debugData the data with useful debugging information
28
+ */
29
+ export const errorLog = (message: string, debugData?: any): void => {
30
+ if (debugData && debugData.code && debugData.message) {
31
+ debugData = {
32
+ code: debugData.code,
33
+ message: debugData.message,
34
+ data: debugData.data
35
+ };
36
+ }
37
+
38
+ if (typeof (<any>window) === 'object') {
39
+ if (!Array.isArray((<any>window).asperaSdkLogs)) {
40
+ (<any>window).asperaSdkLogs = [];
41
+ }
42
+ (<any>window).asperaSdkLogs.push({message, debugData});
43
+ }
44
+
45
+ console.warn(`Aspera SDK: ${message}`, debugData);
46
+ };
47
+
48
+ /**
49
+ * Generate error object for rejecter responses
50
+ *
51
+ * @param message the message indicating the error encountered
52
+ * @param debugData the data with useful debugging information
53
+ *
54
+ * @returns object containing standardized error response
55
+ */
56
+ export const generateErrorBody = (message: string, debugData?: any): ErrorResponse => {
57
+ const errorResponse: ErrorResponse = {
58
+ error: true,
59
+ message
60
+ };
61
+
62
+ if (debugData && debugData.code && debugData.message) {
63
+ errorResponse.debugData = {
64
+ code: debugData.code,
65
+ message: debugData.message,
66
+ data: debugData.data
67
+ };
68
+ }
69
+
70
+ return errorResponse;
71
+ };
72
+
73
+ /**
74
+ * Validate if transferSpec is valid for server communication
75
+ *
76
+ * @param transferSpec the transferSpec to test
77
+ *
78
+ * @returns boolean indicating whether supplied transferSpec is valid
79
+ */
80
+ export const isValidTransferSpec = (transferSpec: TransferSpec): boolean => {
81
+ if (
82
+ transferSpec &&
83
+ typeof transferSpec === 'object' &&
84
+ typeof transferSpec.direction === 'string' &&
85
+ typeof transferSpec.remote_host === 'string' &&
86
+ Array.isArray(transferSpec.paths)
87
+ ) {
88
+ return true;
89
+ }
90
+
91
+ return false;
92
+ };
93
+
94
+ /**
95
+ * Returns a string indicating the websocket URL to use for talking to the server
96
+ *
97
+ * @returns a string of the full Websocket URL
98
+ */
99
+ export const getWebsocketUrl = (serverUrl: string): string => {
100
+ let wsProtocol;
101
+ if (serverUrl.indexOf('http:') === 0) {
102
+ wsProtocol = 'ws';
103
+ } else if (serverUrl.indexOf('https:') === 0) {
104
+ wsProtocol = 'wss';
105
+ }
106
+ const url = serverUrl.replace('http://', '//').replace('https://', '//');
107
+
108
+ return `${wsProtocol}:${url}`;
109
+ };
110
+
111
+ /**
112
+ * Simple function to get the current platform.
113
+ *
114
+ * @returns a string indicating the current platform
115
+ */
116
+ export const getCurrentPlatform = (): 'macos'|'windows'|'linux'|'unknown' => {
117
+ const ua = navigator.userAgent;
118
+
119
+ if (/Mac/.test(ua)) {
120
+ return 'macos';
121
+ } else if (/Win/.test(ua)) {
122
+ return 'windows';
123
+ } else if (/Linux/.test(ua)) {
124
+ return 'linux';
125
+ } else {
126
+ return 'unknown';
127
+ }
128
+ };
129
+
130
+ /**
131
+ * Function used to create a random UUID
132
+ *
133
+ * @returns string
134
+ */
135
+ export const randomUUID = (): string => {
136
+ const fallback = (): string => {
137
+ return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) {
138
+ let r = Number(((new Date().getTime() + 16) * Math.random()).toFixed()) % 16;
139
+ if (c !== 'x') {
140
+ // eslint-disable-next-line no-bitwise
141
+ r = r & 0x3 | 0x8;
142
+ }
143
+ return r.toString(16);
144
+ });
145
+ };
146
+
147
+ return window.crypto?.randomUUID ? window.crypto.randomUUID() : fallback();
148
+ };
149
+
150
+ /**
151
+ * Return a rejected promise
152
+ *
153
+ * @param message the message indicating the error encountered
154
+ * @param debugData the data with useful debugging information
155
+ *
156
+ * @returns a rejected promise
157
+ */
158
+ export const throwError = (message: string, debugData?: any): Promise<any> => {
159
+ errorLog(message, debugData);
160
+ return new Promise((resolve, reject) => {
161
+ reject(generateErrorBody(message, debugData));
162
+ });
163
+ };
164
+
165
+ /**
166
+ * Check if the given string is a valid URL
167
+ *
168
+ * @param url string to check if valid URL
169
+ *
170
+ * @returns boolean
171
+ */
172
+ export const isValidURL = (url: string): boolean => {
173
+ try {
174
+ new URL(url);
175
+ return true;
176
+ } catch(error) {
177
+ return false;
178
+ }
179
+ };
180
+
181
+ /**
182
+ * Checks if the current browser is Safari.
183
+ * @returns {boolean} Whether the browser is Safari.
184
+ */
185
+ export const isSafari = (): boolean => {
186
+ return /^((?!chrome|android).)*safari/i.test(navigator.userAgent) && !(window as any).MSStream;
187
+ };
188
+
189
+ /**
190
+ * Get the URLs for installer management.
191
+ *
192
+ * @returns Info on URLs where installers live
193
+ */
194
+ export const getInstallerUrls = (): InstallerUrlInfo => {
195
+ return {
196
+ base: baseInstallerUrl,
197
+ latest: installerUrl,
198
+ };
199
+ };
200
+
201
+ /**
202
+ * Try to stringify a JSON string and log failures
203
+ *
204
+ * @param json - Object to make into a string
205
+ *
206
+ * @returns string representing JSON or empty string on error
207
+ */
208
+ export const safeJsonString = (json: unknown): string => {
209
+ try {
210
+ return JSON.stringify(json);
211
+ } catch (error) {
212
+ errorLog('safeJsonString: unable to stringify JSON', {error, json});
213
+
214
+ return '';
215
+ }
216
+ };
217
+
218
+ /**
219
+ * Try to parse a JSON string and log failures
220
+ *
221
+ * @param json - String to make into an object
222
+ *
223
+ * @returns object or array from the JSON string. Or undefined
224
+ */
225
+ export const safeJsonParse = (json: string): any|undefined => {
226
+ if (json && typeof json === 'object') {
227
+ return json;
228
+ }
229
+
230
+ try {
231
+ return JSON.parse(json);
232
+ } catch (error) {
233
+ errorLog('safeJsonParse: unable to parse JSON', {error, json});
234
+
235
+ return undefined;
236
+ }
237
+ };
238
+
239
+ export default {
240
+ errorLog,
241
+ generateErrorBody,
242
+ generatePromiseObjects,
243
+ getCurrentPlatform,
244
+ getWebsocketUrl,
245
+ isSafari,
246
+ isValidURL,
247
+ isValidTransferSpec,
248
+ randomUUID,
249
+ throwError,
250
+ getInstallerUrls,
251
+ safeJsonString,
252
+ safeJsonParse,
253
+ };
@@ -0,0 +1,39 @@
1
+ import {generatePromiseObjects} from './helpers';
2
+
3
+ /**
4
+ * Check HTTP promise response for server error response (non 2XX status) and reject promise is error
5
+ *
6
+ * @param promise the HTTP promise to check for HTTP status code of 2XX for success
7
+ *
8
+ * @returns promise for the HTTP connection with catch supporting error
9
+ */
10
+ export const handlePromiseErrors = (promise: Promise<any>): Promise<any> => {
11
+ const promiseInfo = generatePromiseObjects();
12
+ promise.then(response => {
13
+ if (response.ok) {
14
+ promiseInfo.resolver(response);
15
+ } else {
16
+ promiseInfo.rejecter(response);
17
+ }
18
+ return response;
19
+ }).catch(error => {
20
+ promiseInfo.rejecter(error);
21
+ });
22
+ return promiseInfo.promise;
23
+ };
24
+
25
+
26
+ /**
27
+ * Make a GET for retrieving data from a server
28
+ *
29
+ * @param url the url string of the resource on the server
30
+ *
31
+ * @returns a promise that will resolve with the response from the server or reject if network/server error
32
+ */
33
+ export const apiGet = (url: string): Promise<any> => {
34
+ return handlePromiseErrors(fetch(url, {
35
+ headers: {
36
+ 'Content-Type': 'application/json',
37
+ },
38
+ }));
39
+ };
@@ -0,0 +1,191 @@
1
+ import {errorLog, generatePromiseObjects, getWebsocketUrl, safeJsonParse, safeJsonString} from './helpers';
2
+ import {messages} from '../constants/messages';
3
+ import {asperaSdk} from '../index';
4
+ import {TransferResponse} from '../models/aspera-sdk.model';
5
+ import {WebsocketEvent, WebsocketMessage, WebsocketTopics} from '../models/models';
6
+
7
+ export class WebsocketService {
8
+ /** The main websocket connection to Aspera App*/
9
+ private globalSocket: WebSocket;
10
+ /** A map of requested subscription names and the callback for them */
11
+ private sockets: Map<WebsocketTopics, Function> = new Map();
12
+ /** The callback for websocket events */
13
+ private eventListener: Function;
14
+ /** Indicator if the websocket is already connected */
15
+ private isConnected = false;
16
+ /** Global promise object that resolves when init completes */
17
+ private initPromise = generatePromiseObjects();
18
+
19
+ /** Log call for not being ready */
20
+ private handleNotReady(): void {
21
+ errorLog(messages.websocketNotReady);
22
+ }
23
+
24
+ /**
25
+ * This function handles when a connection is opened
26
+ */
27
+ private handleOpen = (): void => {
28
+ if (this.isConnected || !this.joinChannel()) {
29
+ return;
30
+ }
31
+
32
+ this.isConnected = true;
33
+ this.updateRpcPort();
34
+ this.notifyEvent('RECONNECT');
35
+ };
36
+
37
+ /**
38
+ * This function handles completed subscription
39
+ */
40
+ private handleClose = (): void => {
41
+ if (this.isConnected) {
42
+ this.isConnected = false;
43
+ this.notifyEvent('CLOSED');
44
+ }
45
+
46
+ if (!this.globalSocket) {
47
+ this.handleNotReady();
48
+ return;
49
+ }
50
+
51
+ this.reconnect();
52
+ };
53
+
54
+ /**
55
+ * This function handles errors received from the websocket
56
+ */
57
+ private handleError = (): void => {
58
+ errorLog(messages.websocketClosedError);
59
+ };
60
+
61
+ /**
62
+ * This function handles messages received from the websocket
63
+ */
64
+ private handleMessage = (message: MessageEvent<string>): void => {
65
+ const data: WebsocketMessage|undefined = safeJsonParse(message.data);
66
+
67
+ // Message we get on subscription
68
+ if (data && data.id === 1) {
69
+ this.initPromise.resolver(data);
70
+
71
+ return;
72
+ }
73
+
74
+ const socket = this.sockets.get(data.method);
75
+
76
+ if (typeof socket === 'function' && data.params) {
77
+ socket(data.params);
78
+ }
79
+ };
80
+
81
+ /**
82
+ * This function joins the channel to be able to subscribe to events
83
+ */
84
+ private joinChannel(): boolean {
85
+ if (!this.globalSocket) {
86
+ this.handleNotReady();
87
+ return false;
88
+ }
89
+
90
+ this.globalSocket.send(safeJsonString({jsonrpc: '2.0', method: 'subscribe_transfer_activity', params: [asperaSdk.globals.appId], id: 1}));
91
+
92
+ return true;
93
+ }
94
+
95
+ /**
96
+ * This function registers clients to listen to a certain message name. Returns any to allow functions to declare proper type
97
+ *
98
+ * @param messageName - the name of messages to listen to (one message name per subscription)
99
+ * @param callback - the callback function
100
+ */
101
+ registerMessage(messageName: WebsocketTopics, callback: Function): void {
102
+ if (!this.sockets.get(messageName)) {
103
+ this.sockets.set(messageName, (data: {result: TransferResponse}) => {
104
+ callback(data.result);
105
+ });
106
+ }
107
+ }
108
+
109
+ /**
110
+ *
111
+ * @param callback This function registers clients to a certain WebSocket event.
112
+ *
113
+ * @param callback - the callback function to call with the event name.
114
+ */
115
+ registerEvent(callback: Function): void {
116
+ this.eventListener = callback;
117
+ this.eventListener(this.isConnected ? 'RECONNECT': 'CLOSED');
118
+ }
119
+
120
+ /**
121
+ * This function starts the websocket subscription with the websocket provider
122
+ *
123
+ * @returns a promise that resolves when the websocket connection is established
124
+ */
125
+ init(): Promise<unknown> {
126
+ this.connect();
127
+
128
+ return this.initPromise.promise;
129
+ }
130
+
131
+ private connect() {
132
+ this.getWebSocketConnection(asperaSdk.globals.rpcPort)
133
+ .then((webSocket) => {
134
+ this.globalSocket = webSocket;
135
+ this.globalSocket.onerror = this.handleError;
136
+ this.globalSocket.onclose = this.handleClose;
137
+ this.globalSocket.onopen = this.handleOpen;
138
+ this.globalSocket.onmessage = this.handleMessage;
139
+
140
+ this.handleOpen();
141
+ }).catch(() => {
142
+ this.reconnect();
143
+ });
144
+ }
145
+
146
+ private reconnect() {
147
+ if (this.globalSocket) {
148
+ this.globalSocket.close();
149
+ }
150
+
151
+ setTimeout(() => {
152
+ this.connect();
153
+ }, 1000);
154
+ }
155
+
156
+ private getWebSocketConnection(port: number): Promise<WebSocket> {
157
+ const webSocketUrl = getWebsocketUrl(asperaSdk.globals.asperaAppUrl);
158
+
159
+ return new Promise((resolve, reject) => {
160
+ const webSocket = new WebSocket(`${webSocketUrl}:${port}`);
161
+
162
+ webSocket.onopen = () => {
163
+ resolve(webSocket);
164
+ };
165
+
166
+ webSocket.onerror = () => {
167
+ reject(`Connection failed on port ${port}`);
168
+ };
169
+ });
170
+ }
171
+
172
+ private notifyEvent(event: WebsocketEvent) {
173
+ if (typeof this.eventListener === 'function') {
174
+ this.eventListener(event);
175
+ }
176
+ }
177
+
178
+ private updateRpcPort() {
179
+ if (!this.globalSocket) {
180
+ return;
181
+ }
182
+
183
+ const url = new URL(this.globalSocket.url);
184
+
185
+ asperaSdk.globals.rpcPort = Number(url.port);
186
+ }
187
+ }
188
+
189
+ export const websocketService = new WebsocketService();
190
+
191
+ export default WebsocketService;