@orfeas126/box-ui-elements
Version:
Box UI Elements
268 lines (255 loc) • 10.3 kB
JavaScript
function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; }
function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { _defineProperty(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; }
function _defineProperty(e, r, t) { return (r = _toPropertyKey(r)) in e ? Object.defineProperty(e, r, { value: t, enumerable: !0, configurable: !0, writable: !0 }) : e[r] = t, e; }
function _toPropertyKey(t) { var i = _toPrimitive(t, "string"); return "symbol" == typeof i ? i : i + ""; }
function _toPrimitive(t, r) { if ("object" != typeof t || !t) return t; var e = t[Symbol.toPrimitive]; if (void 0 !== e) { var i = e.call(t, r || "default"); if ("object" != typeof i) return i; throw new TypeError("@@toPrimitive must return a primitive value."); } return ("string" === r ? String : Number)(t); }
/**
*
* @file Helper for the box file API
* @author Box
*/
import queryString from 'query-string';
import getProp from 'lodash/get';
import { findMissingProperties, fillMissingProperties } from '../utils/fields';
import { getTypedFileId } from '../utils/file';
import { getBadItemError, getBadPermissionsError } from '../utils/error';
import { CACHE_PREFIX_FILE, ERROR_CODE_FETCH_FILE, ERROR_CODE_GET_DOWNLOAD_URL, FIELD_AUTHENTICATED_DOWNLOAD_URL, FIELD_EXTENSION, FIELD_IS_DOWNLOAD_AVAILABLE, REPRESENTATIONS_RESPONSE_ERROR, REPRESENTATIONS_RESPONSE_SUCCESS, REPRESENTATIONS_RESPONSE_VIEWABLE, X_REP_HINTS } from '../constants';
import Item from './Item';
import { retryNumOfTimes } from '../utils/function';
import TokenService from '../utils/TokenService';
class File extends Item {
/**
* Creates a key for the cache
*
* @param {string} id - Folder id
* @return {string} key
*/
getCacheKey(id) {
return `${CACHE_PREFIX_FILE}${id}`;
}
/**
* API URL for files
*
* @param {string} [id] - Optional file id
* @return {string} base url for files
*/
getUrl(id) {
const suffix = id ? `/${id}` : '';
return `${this.getBaseApiUrl()}/files${suffix}`;
}
/**
* API for getting download URL for files and file versions
*
* @param {string} fileId - File id
* @param {BoxItem|BoxItemVersion} fileOrFileVersion - File or file version to download
* @param {Function} successCallback - Success callback
* @param {Function} errorCallback - Error callback
* @return {void}
*/
async getDownloadUrl(fileId, fileOrFileVersion, successCallback, errorCallback) {
this.errorCode = ERROR_CODE_GET_DOWNLOAD_URL;
this.errorCallback = errorCallback;
this.successCallback = successCallback;
const downloadAvailable = fileOrFileVersion[FIELD_IS_DOWNLOAD_AVAILABLE];
const downloadUrl = fileOrFileVersion[FIELD_AUTHENTICATED_DOWNLOAD_URL];
const token = await TokenService.getReadToken(getTypedFileId(fileId), this.options.token);
if (!downloadAvailable || !downloadUrl || !token) {
this.errorHandler(new Error('Download is missing required fields or token.'));
return;
}
const {
query,
url: downloadBaseUrl
} = queryString.parseUrl(downloadUrl);
const downloadUrlParams = _objectSpread(_objectSpread({}, query), {}, {
access_token: token
});
const downloadUrlQuery = queryString.stringify(downloadUrlParams);
this.successHandler(`${downloadBaseUrl}?${downloadUrlQuery}`);
}
/**
* Determines whether the call to the file representations API has completed
*
* @param {data: { FileRepresentation }} response
* @return {boolean}
*/
isRepresentationsCallComplete(response) {
const status = getProp(response, 'data.status.state');
return !status || status === REPRESENTATIONS_RESPONSE_ERROR || status === REPRESENTATIONS_RESPONSE_SUCCESS || status === REPRESENTATIONS_RESPONSE_VIEWABLE;
}
/**
* Polls a representation's infoUrl, attempting to generate a representation
*
* @param {FileRepresentation} representation - representation that should have its info.url polled
* @return {Promise<FileRepresentation>} - representation updated with most current status
*/
async generateRepresentation(representation) {
const infoUrl = getProp(representation, 'info.url');
if (!infoUrl) {
return representation;
}
return retryNumOfTimes((successCallback, errorCallback) => this.xhr.get({
successCallback,
errorCallback,
url: infoUrl
}).then(response => this.isRepresentationsCallComplete(response) ? successCallback(response.data) : errorCallback(response.data)).catch(e => {
errorCallback(e);
}), 4, 2000, 2);
}
/**
* API for getting a thumbnail URL for a BoxItem
*
* @param {BoxItem} item - BoxItem to get the thumbnail URL for
* @return {Promise<?string>} - the url for the item's thumbnail, or null
*/
async getThumbnailUrl(item) {
const entry = getProp(item, 'representations.entries[0]');
const extension = getProp(entry, 'representation');
const template = getProp(entry, 'content.url_template');
const token = await TokenService.getReadToken(getTypedFileId(item.id), this.options.token);
if (!extension || !template || !token) {
return null;
}
const thumbnailUrl = template.replace('{+asset_path}', extension === 'jpg' ? '' : '1.png');
const {
query,
url: thumbnailBaseUrl
} = queryString.parseUrl(thumbnailUrl);
const thumbnailUrlParams = _objectSpread(_objectSpread({}, query), {}, {
access_token: token
});
const thumbnailUrlQuery = queryString.stringify(thumbnailUrlParams);
return `${thumbnailBaseUrl}?${thumbnailUrlQuery}`;
}
/**
* API for setting the description of a file
*
* @param {BoxItem} file - File object for which we are changing the description
* @param {string} description - New file description
* @param {Function} successCallback - Success callback
* @param {Function} errorCallback - Error callback
* @return {Promise}
*/
setFileDescription(file, description, successCallback, errorCallback) {
const {
id,
permissions
} = file;
if (!id || !permissions) {
errorCallback(getBadItemError());
return Promise.reject();
}
if (!permissions.can_rename) {
errorCallback(getBadPermissionsError());
return Promise.reject();
}
return this.xhr.put({
id: getTypedFileId(id),
url: this.getUrl(id),
data: {
description
}
}).then(({
data
}) => {
if (!this.isDestroyed()) {
const updatedFile = this.merge(this.getCacheKey(id), 'description', data.description);
successCallback(updatedFile);
}
}).catch(() => {
if (!this.isDestroyed()) {
const originalFile = this.merge(this.getCacheKey(id), 'description', file.description);
errorCallback(originalFile);
}
});
}
/**
* Gets a box file
*
* @param {string} id - File id
* @param {Function} successCallback - Function to call with results
* @param {Function} errorCallback - Function to call with errors
* @param {boolean|void} [options.fields] - Optionally include specific fields
* @param {boolean|void} [options.forceFetch] - Optionally Bypasses the cache
* @param {boolean|void} [options.refreshCache] - Optionally Updates the cache
* @return {Promise}
*/
async getFile(id, successCallback, errorCallback, options = {}) {
if (this.isDestroyed()) {
return;
}
const cache = this.getCache();
const key = this.getCacheKey(id);
const isCached = !options.forceFetch && cache.has(key);
const file = isCached ? cache.get(key) : {
id
};
let missingFields = findMissingProperties(file, options.fields);
const xhrOptions = {
id: getTypedFileId(id),
url: this.getUrl(id),
headers: {
'X-Rep-Hints': X_REP_HINTS
}
};
this.errorCode = ERROR_CODE_FETCH_FILE;
this.successCallback = successCallback;
this.errorCallback = errorCallback;
// If the file was cached and there are no missing fields
// then just return the cached file and optionally refresh
// the cache with new data if required
if (isCached && missingFields.length === 0) {
successCallback(file);
missingFields = options.fields || [];
if (!options.refreshCache) {
return;
}
}
// If there are missing fields to fetch, add it to the params
if (missingFields.length > 0) {
xhrOptions.params = {
fields: missingFields.toString()
};
}
try {
const {
data
} = await this.xhr.get(xhrOptions);
if (this.isDestroyed()) {
return;
}
// Merge fields that were requested but were actually not returned.
// This part is mostly useful for metadata.foo.bar fields since the API
// returns { metadata: null } instead of { metadata: { foo: { bar: null } } }
const dataWithMissingFields = fillMissingProperties(data, missingFields);
// Cache check is again done since this code is executed async
if (cache.has(key)) {
cache.merge(key, dataWithMissingFields);
} else {
// If there was nothing in the cache
cache.set(key, dataWithMissingFields);
}
this.successHandler(cache.get(key));
} catch (e) {
this.errorHandler(e);
}
}
/**
* Gets the extension of a box file.
*
* @param {string} id - File id
* @param {Function} successCallback - Function to call with results
* @param {Function} errorCallback - Function to call with errors
* @return {Promise}
*/
getFileExtension(id, successCallback, errorCallback) {
if (this.isDestroyed()) {
return;
}
this.getFile(id, successCallback, errorCallback, {
fields: [FIELD_EXTENSION]
});
}
}
export default File;
//# sourceMappingURL=File.js.map