diff --git a/src/tools/isReadyPoll.ts b/src/tools/isReadyPoll.ts new file mode 100644 index 000000000..a6e02725c --- /dev/null +++ b/src/tools/isReadyPoll.ts @@ -0,0 +1,47 @@ +import info from '../api/info' +import { poll } from './poll' +import { FileInfo } from '../api/types' +import CancelController from './CancelController' + +type ArgsIsReadyPool = { + file: string + publicKey: string + baseURL?: string + source?: string + integration?: string + retryThrottledRequestMaxTimes?: number + onProgress?: (args: { value: number }) => void + cancel?: CancelController +} + +function isReadyPoll({ + file, + publicKey, + baseURL, + source, + integration, + retryThrottledRequestMaxTimes, + cancel, + onProgress +}: ArgsIsReadyPool): FileInfo | PromiseLike { + return poll({ + check: cancel => + info(file, { + publicKey, + baseURL, + cancel, + source, + integration, + retryThrottledRequestMaxTimes + }).then(response => { + if (response.isReady) { + return response + } + onProgress && onProgress({ value: 1 }) + return false + }), + cancel + }) +} + +export { isReadyPoll } diff --git a/src/uploadFile/pollStrategy.ts b/src/uploadFile/pollStrategy.ts deleted file mode 100644 index 885b0c17d..000000000 --- a/src/uploadFile/pollStrategy.ts +++ /dev/null @@ -1,62 +0,0 @@ -import fromUrlStatus, { Status } from '../api/fromUrlStatus' -import { poll } from '../tools/poll' -import { UploadClientError } from '../tools/errors' -import { FileInfo } from '../api/types' -import CancelController from '../tools/CancelController' - -function pollStrategy({ - token, - publicKey, - baseURL, - integration, - retryThrottledRequestMaxTimes, - onProgress, - cancel -}: { - token: string - publicKey: string - baseURL?: string - integration?: string - retryThrottledRequestMaxTimes?: number - onProgress?: (info: { value: number }) => void - cancel?: CancelController -}): Promise { - return poll({ - check: cancel => - fromUrlStatus(token, { - publicKey, - baseURL, - integration, - retryThrottledRequestMaxTimes, - cancel - }).then(response => { - switch (response.status) { - case Status.Error: { - return new UploadClientError(response.error) - } - case Status.Waiting: { - return false - } - case Status.Unknown: { - return new UploadClientError(`Token "${token}" was not found.`) - } - case Status.Progress: { - if (onProgress) - onProgress({ value: response.done / response.total }) - return false - } - case Status.Success: { - if (onProgress) - onProgress({ value: response.done / response.total }) - return response - } - default: { - throw new Error('Unknown status') - } - } - }), - cancel - }) -} - -export default pollStrategy diff --git a/src/uploadFile/pushStrategy.ts b/src/uploadFile/pushStrategy.ts deleted file mode 100644 index 3b3a3f47a..000000000 --- a/src/uploadFile/pushStrategy.ts +++ /dev/null @@ -1,76 +0,0 @@ -import CancelController from '../tools/CancelController' -import { UploadClientError, cancelError } from '../tools/errors' -import Pusher from './pusher' - -import { FileInfo } from '../api/types' -import { Status } from '../api/fromUrlStatus' - -// no timeout for nodeJS and 30000 ms for browser -const disconectTimeout = typeof window === 'undefined' ? 0 : 30000 - -let pusher: Pusher | null = null -const getPusher = (key: string): Pusher => { - if (!pusher) { - pusher = new Pusher(key, disconectTimeout) - } - - return pusher -} - -const pushStrategy = ({ - token, - pusherKey, - cancel, - stopRace, - onProgress -}: { - token: string - pusherKey: string - cancel: CancelController - stopRace: () => void - onProgress?: (info: { value: number }) => void -}): Promise => - new Promise((resolve, reject) => { - const pusher = getPusher(pusherKey) - const unsubErrorHandler = pusher.onError(reject) - - cancel.onCancel(() => { - unsubErrorHandler() - pusher.unsubscribe(token) - reject(cancelError('pisher cancelled')) - }) - - pusher.subscribe(token, result => { - stopRace() - - switch (result.status) { - case Status.Success: { - unsubErrorHandler() - pusher.unsubscribe(token) - if (onProgress) onProgress({ value: result.done / result.total }) - resolve(result) - break - } - - case Status.Progress: { - if (onProgress) { - onProgress({ value: result.done / result.total }) - } - break - } - - case Status.Error: { - unsubErrorHandler() - pusher.unsubscribe(token) - reject(new UploadClientError(result.msg)) - } - } - }) - }) - -const preconnect = (key: string): void => { - getPusher(key).connect() -} - -export default pushStrategy -export { preconnect, pushStrategy } diff --git a/src/uploadFile/pusher.ts b/src/uploadFile/pusher.ts index f277dd7ac..4c830b441 100644 --- a/src/uploadFile/pusher.ts +++ b/src/uploadFile/pusher.ts @@ -182,4 +182,20 @@ class Pusher { } } +let pusher: Pusher | null = null +const getPusher = (key: string): Pusher => { + if (!pusher) { + // no timeout for nodeJS and 30000 ms for browser + const disconectTimeout = typeof window === 'undefined' ? 0 : 30000 + pusher = new Pusher(key, disconectTimeout) + } + + return pusher +} + +const preconnect = (key: string): void => { + getPusher(key).connect() +} + export default Pusher +export { getPusher, preconnect } diff --git a/src/uploadFile/uploadBase.ts b/src/uploadFile/uploadBase.ts index ac5322765..76682c96a 100644 --- a/src/uploadFile/uploadBase.ts +++ b/src/uploadFile/uploadBase.ts @@ -1,12 +1,9 @@ import base from '../api/base' -import info from '../api/info' -import { poll } from '../tools/poll' import { UploadcareFile } from '../tools/UploadcareFile' import CancelController from '../tools/CancelController' -/* Types */ -import { FileInfo } from '../api/types' import { NodeFile, BrowserFile } from '../request/types' +import { isReadyPoll } from '../tools/isReadyPoll' type FromObjectOptions = { publicKey: string @@ -50,13 +47,6 @@ const uploadFromObject = ( baseCDN }: FromObjectOptions ): Promise => { - let progress: number - - const onProgressCallback = ({ value }): void => { - progress = value * 0.98 - onProgress && onProgress({ value: progress }) - } - return base(file, { publicKey, fileName, @@ -65,36 +55,21 @@ const uploadFromObject = ( secureExpire, store, cancel, - onProgress: onProgress ? onProgressCallback : undefined, + onProgress, source, integration, retryThrottledRequestMaxTimes }) .then(({ file }) => { - return poll({ - check: cancel => - info(file, { - publicKey, - baseURL, - cancel, - source, - integration, - retryThrottledRequestMaxTimes - }).then(response => { - if (response.isReady) { - onProgress && onProgress({ value: 1 }) - - return response - } - - if (onProgress) { - onProgress({ - value: Math.min(progress + 0.02, 1) - }) - } - - return false - }) + return isReadyPoll({ + file, + publicKey, + baseURL, + source, + integration, + retryThrottledRequestMaxTimes, + onProgress, + cancel }) }) .then(fileInfo => new UploadcareFile(fileInfo, { baseCDN })) diff --git a/src/uploadFile/uploadFromUrl.ts b/src/uploadFile/uploadFromUrl.ts index 88426b71b..118b39877 100644 --- a/src/uploadFile/uploadFromUrl.ts +++ b/src/uploadFile/uploadFromUrl.ts @@ -1,16 +1,125 @@ +import fromUrlStatus, { Status } from '../api/fromUrlStatus' import fromUrl, { TypeEnum } from '../api/fromUrl' -import { UploadClientError } from '../tools/errors' +import { UploadClientError, cancelError } from '../tools/errors' +import { poll } from '../tools/poll' import { race } from '../tools/race' +import { isReadyPoll } from '../tools/isReadyPoll' import defaultSettings from '../defaultSettings' -import poll from './pollStrategy' -import push, { preconnect } from './pushStrategy' +import { getPusher, preconnect } from './pusher' /* Types */ import { FileInfo } from '../api/types' import CancelController from '../tools/CancelController' import { UploadcareFile } from '../tools/UploadcareFile' +function pollStrategy({ + token, + publicKey, + baseURL, + integration, + retryThrottledRequestMaxTimes, + onProgress, + cancel +}: { + token: string + publicKey: string + baseURL?: string + integration?: string + retryThrottledRequestMaxTimes?: number + onProgress?: (info: { value: number }) => void + cancel?: CancelController +}): Promise { + return poll({ + check: cancel => + fromUrlStatus(token, { + publicKey, + baseURL, + integration, + retryThrottledRequestMaxTimes, + cancel + }).then(response => { + switch (response.status) { + case Status.Error: { + return new UploadClientError(response.error) + } + case Status.Waiting: { + return false + } + case Status.Unknown: { + return new UploadClientError(`Token "${token}" was not found.`) + } + case Status.Progress: { + if (onProgress) + onProgress({ value: response.done / response.total }) + return false + } + case Status.Success: { + if (onProgress) + onProgress({ value: response.done / response.total }) + return response + } + default: { + throw new Error('Unknown status') + } + } + }), + cancel + }) +} + +const pushStrategy = ({ + token, + pusherKey, + cancel, + stopRace, + onProgress +}: { + token: string + pusherKey: string + cancel: CancelController + stopRace: () => void + onProgress?: (info: { value: number }) => void +}): Promise => + new Promise((resolve, reject) => { + const pusher = getPusher(pusherKey) + const unsubErrorHandler = pusher.onError(reject) + const destroy = (): void => { + unsubErrorHandler() + pusher.unsubscribe(token) + } + + cancel.onCancel(() => { + destroy() + reject(cancelError('pisher cancelled')) + }) + + pusher.subscribe(token, result => { + stopRace() + + switch (result.status) { + case Status.Progress: { + if (onProgress) { + onProgress({ value: result.done / result.total }) + } + break + } + + case Status.Success: { + destroy() + if (onProgress) onProgress({ value: result.done / result.total }) + resolve(result) + break + } + + case Status.Error: { + destroy() + reject(new UploadClientError(result.msg)) + } + } + }) + }) + type FromUrlOptions = { publicKey: string fileName?: string @@ -73,7 +182,7 @@ const uploadFromUrl = ( return race( [ ({ cancel }): Promise => - poll({ + pollStrategy({ token: urlResponse.token, publicKey, baseURL, @@ -83,23 +192,13 @@ const uploadFromUrl = ( cancel }), ({ stopRace, cancel }): Promise => - push({ + pushStrategy({ token: urlResponse.token, pusherKey, stopRace, cancel, onProgress - }).then(() => - poll({ - token: urlResponse.token, - publicKey, - baseURL, - integration, - retryThrottledRequestMaxTimes, - onProgress, - cancel - }) - ) + }) ], { cancel } ) @@ -110,6 +209,17 @@ const uploadFromUrl = ( return result }) + .then(result => + isReadyPoll({ + file: result.uuid, + publicKey, + baseURL, + integration, + retryThrottledRequestMaxTimes, + onProgress, + cancel + }) + ) .then(fileInfo => new UploadcareFile(fileInfo, { baseCDN })) export default uploadFromUrl