UNPKG

dropbox

Version:

The Dropbox JavaScript SDK is a lightweight, promise based interface to the Dropbox v2 API that works in both nodejs and browser environments.

397 lines (348 loc) 12.1 kB
import { UPLOAD, DOWNLOAD, RPC } from './constants'; import { downloadRequest } from './download-request'; import { uploadRequest } from './upload-request'; import { rpcRequest } from './rpc-request'; /* eslint-disable */ // Polyfill object.assign for legacy browsers // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/assign if (typeof Object.assign !== 'function') { (function () { Object.assign = function (target) { 'use strict'; var output; var index; var source; var nextKey; if (target === undefined || target === null) { throw new TypeError('Cannot convert undefined or null to object'); } output = Object(target); for (index = 1; index < arguments.length; index++) { source = arguments[index]; if (source !== undefined && source !== null) { for (nextKey in source) { if (source.hasOwnProperty(nextKey)) { output[nextKey] = source[nextKey]; } } } } return output; }; }()); } // Polyfill Array.includes for legacy browsers // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/includes // https://tc39.github.io/ecma262/#sec-array.prototype.includes if (!Array.prototype.includes) { Object.defineProperty(Array.prototype, 'includes', { value: function(searchElement, fromIndex) { if (this == null) { throw new TypeError('"this" is null or not defined'); } // 1. Let O be ? ToObject(this value). var o = Object(this); // 2. Let len be ? ToLength(? Get(O, "length")). var len = o.length >>> 0; // 3. If len is 0, return false. if (len === 0) { return false; } // 4. Let n be ? ToInteger(fromIndex). // (If fromIndex is undefined, this step produces the value 0.) var n = fromIndex | 0; // 5. If n ≥ 0, then // a. Let k be n. // 6. Else n < 0, // a. Let k be len + n. // b. If k < 0, let k be 0. var k = Math.max(n >= 0 ? n : len - Math.abs(n), 0); function sameValueZero(x, y) { return x === y || (typeof x === 'number' && typeof y === 'number' && isNaN(x) && isNaN(y)); } // 7. Repeat, while k < len while (k < len) { // a. Let elementK be the result of ? Get(O, ! ToString(k)). // b. If SameValueZero(searchElement, elementK) is true, return true. if (sameValueZero(o[k], searchElement)) { return true; } // c. Increase k by 1. k++; } // 8. Return false return false; } }); } /* eslint-enable */ /** * @private * @class DropboxBase * @classdesc The main Dropbox SDK class. This contains the methods that are * shared between Dropbox and DropboxTeam classes. It is marked as private so * that it doesn't show up in the docs because it is never used directly. * @arg {Object} options * @arg {Function} [options.fetch] - fetch library for making requests. * @arg {String} [options.accessToken] - An access token for making authenticated * requests. * @arg {String} [options.clientId] - The client id for your app. Used to create * authentication URL. * @arg {String} [options.clientSecret] - The client secret for your app. * @arg {Number} [options.selectUser] - User that the team access token would like * to act as. * @arg {String} [options.selectAdmin] - Team admin that the team access token would like * to act as. * @arg {String} [options.pathRoot] - root pass to access other namespaces * Use to access team folders for example */ function parseBodyToType(res) { const clone = res.clone(); return new Promise((resolve) => { res.json() .then(data => resolve(data)) .catch(() => clone.text().then(data => resolve(data))); }).then(data => [res, data]); } export class DropboxBase { constructor(options) { options = options || {}; this.accessToken = options.accessToken; this.clientId = options.clientId; this.clientSecret = options.clientSecret; this.selectUser = options.selectUser; this.selectAdmin = options.selectAdmin; this.fetch = options.fetch || fetch; this.pathRoot = options.pathRoot; if (!options.fetch) { console.warn('Global fetch is deprecated and will be unsupported in a future version. Please pass fetch function as option when instantiating dropbox instance: new Dropbox({fetch})'); } // eslint-disable-line no-console } /** * Set the access token used to authenticate requests to the API. * @arg {String} accessToken - An access token * @returns {undefined} */ setAccessToken(accessToken) { this.accessToken = accessToken; } /** * Get the access token * @returns {String} Access token */ getAccessToken() { return this.accessToken; } /** * Set the client id, which is used to help gain an access token. * @arg {String} clientId - Your apps client id * @returns {undefined} */ setClientId(clientId) { this.clientId = clientId; } /** * Get the client id * @returns {String} Client id */ getClientId() { return this.clientId; } /** * Set the client secret * @arg {String} clientSecret - Your app's client secret * @returns {undefined} */ setClientSecret(clientSecret) { this.clientSecret = clientSecret; } /** * Get the client secret * @returns {String} Client secret */ getClientSecret() { return this.clientSecret; } /** * Get a URL that can be used to authenticate users for the Dropbox API. * @arg {String} redirectUri - A URL to redirect the user to after * authenticating. This must be added to your app through the admin interface. * @arg {String} [state] - State that will be returned in the redirect URL to help * prevent cross site scripting attacks. * @arg {String} [authType] - auth type, defaults to 'token', other option is 'code' * @returns {String} Url to send user to for Dropbox API authentication */ getAuthenticationUrl(redirectUri, state, authType = 'token') { const clientId = this.getClientId(); const baseUrl = 'https://www.dropbox.com/oauth2/authorize'; if (!clientId) { throw new Error('A client id is required. You can set the client id using .setClientId().'); } if (authType !== 'code' && !redirectUri) { throw new Error('A redirect uri is required.'); } if (!['code', 'token'].includes(authType)) { throw new Error('Authorization type must be code or token'); } let authUrl; if (authType === 'code') { authUrl = `${baseUrl}?response_type=code&client_id=${clientId}`; } else { authUrl = `${baseUrl}?response_type=token&client_id=${clientId}`; } if (redirectUri) { authUrl += `&redirect_uri=${redirectUri}`; } if (state) { authUrl += `&state=${state}`; } return authUrl; } /** * Get an OAuth2 access token from an OAuth2 Code. * @arg {String} redirectUri - A URL to redirect the user to after * authenticating. This must be added to your app through the admin interface. * @arg {String} code - An OAuth2 code. */ getAccessTokenFromCode(redirectUri, code) { const clientId = this.getClientId(); const clientSecret = this.getClientSecret(); if (!clientId) { throw new Error('A client id is required. You can set the client id using .setClientId().'); } if (!clientSecret) { throw new Error('A client secret is required. You can set the client id using .setClientSecret().'); } const path = `https://api.dropboxapi.com/oauth2/token?code=${code}&\ grant_type=authorization_code&redirect_uri=${redirectUri}&\ client_id=${clientId}&client_secret=${clientSecret}`; const fetchOptions = { method: 'POST', headers: { 'Content-Type': 'application/x-www-form-urlencoded', }, }; return this.fetch(path, fetchOptions) .then(res => parseBodyToType(res)) .then(([res, data]) => { // maintaining existing API for error codes not equal to 200 range if (!res.ok) { // eslint-disable-next-line no-throw-literal throw { error: data, response: res, status: res.status, }; } return data.access_token; }); } /** * Called when the authentication succeed * @callback successCallback * @param {string} access_token The application's access token */ /** * Called when the authentication failed. * @callback errorCallback */ /** * An authentication process that works with cordova applications. * @param {successCallback} successCallback * @param {errorCallback} errorCallback */ authenticateWithCordova(successCallback, errorCallback) { const redirectUrl = 'https://www.dropbox.com/1/oauth2/redirect_receiver'; const url = this.getAuthenticationUrl(redirectUrl); let removed = false; const browser = window.open(url, '_blank'); function onLoadError(event) { if (event.code !== -999) { // Workaround to fix wrong behavior on cordova-plugin-inappbrowser // Try to avoid a browser crash on browser.close(). window.setTimeout(() => { browser.close(); }, 10); errorCallback(); } } function onLoadStop(event) { const errorLabel = '&error='; const errorIndex = event.url.indexOf(errorLabel); if (errorIndex > -1) { // Try to avoid a browser crash on browser.close(). window.setTimeout(() => { browser.close(); }, 10); errorCallback(); } else { const tokenLabel = '#access_token='; let tokenIndex = event.url.indexOf(tokenLabel); const tokenTypeIndex = event.url.indexOf('&token_type='); if (tokenIndex > -1) { tokenIndex += tokenLabel.length; // Try to avoid a browser crash on browser.close(). window.setTimeout(() => { browser.close(); }, 10); const accessToken = event.url.substring(tokenIndex, tokenTypeIndex); successCallback(accessToken); } } } function onExit() { if (removed) { return; } browser.removeEventListener('loaderror', onLoadError); browser.removeEventListener('loadstop', onLoadStop); browser.removeEventListener('exit', onExit); removed = true; } browser.addEventListener('loaderror', onLoadError); browser.addEventListener('loadstop', onLoadStop); browser.addEventListener('exit', onExit); } request(path, args, auth, host, style) { let request = null; switch (style) { case RPC: request = this.getRpcRequest(); break; case DOWNLOAD: request = this.getDownloadRequest(); break; case UPLOAD: request = this.getUploadRequest(); break; default: throw new Error(`Invalid request style: ${style}`); } const options = { selectUser: this.selectUser, selectAdmin: this.selectAdmin, clientId: this.getClientId(), clientSecret: this.getClientSecret(), pathRoot: this.pathRoot, }; return request(path, args, auth, host, this.getAccessToken(), options); } setRpcRequest(newRpcRequest) { this.rpcRequest = newRpcRequest; } getRpcRequest() { if (this.rpcRequest === undefined) { this.rpcRequest = rpcRequest(this.fetch); } return this.rpcRequest; } setDownloadRequest(newDownloadRequest) { this.downloadRequest = newDownloadRequest; } getDownloadRequest() { if (this.downloadRequest === undefined) { this.downloadRequest = downloadRequest(this.fetch); } return this.downloadRequest; } setUploadRequest(newUploadRequest) { this.uploadRequest = newUploadRequest; } getUploadRequest() { if (this.uploadRequest === undefined) { this.uploadRequest = uploadRequest(this.fetch); } return this.uploadRequest; } }