box-node-sdk
Version:
Official SDK for Box Plaform APIs
1,098 lines • 65 kB
JavaScript
"use strict";
/**
* @fileoverview Manager for the Box Files Resource
*/
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
// ------------------------------------------------------------------------------
// Requirements
// ------------------------------------------------------------------------------
const bluebird_1 = __importDefault(require("bluebird"));
const crypto_1 = __importDefault(require("crypto"));
const http_status_1 = __importDefault(require("http-status"));
const stream_1 = require("stream");
const url_template_1 = __importDefault(require("url-template"));
const errors_1 = __importDefault(require("../util/errors"));
const url_path_1 = __importDefault(require("../util/url-path"));
const ChunkedUploader = require('../chunked-uploader');
// -----------------------------------------------------------------------------
// Typedefs
// -----------------------------------------------------------------------------
/**
* Enum of valid x-rep- hint values for generating representation info
*
* @readonly
* @enum {FileRepresentationType}
*/
var FileRepresentationType;
(function (FileRepresentationType) {
FileRepresentationType["PDF"] = "[pdf]";
FileRepresentationType["THUMBNAIL"] = "[jpg?dimensions=320x320]";
FileRepresentationType["IMAGE_MEDIUM"] = "[jpg?dimensions=1024x1024][png?dimensions=1024x1024]";
FileRepresentationType["IMAGE_LARGE"] = "[jpg?dimensions=2048x2048][png?dimensions=2048x2048]";
FileRepresentationType["EXTRACTED_TEXT"] = "[extracted_text]";
})(FileRepresentationType || (FileRepresentationType = {}));
/**
* Enum of valid lock types
*
* @readonly
* @enum {LockType}
*/
var LockType;
(function (LockType) {
LockType["LOCK"] = "lock";
LockType["UNLOCK"] = "unlock";
})(LockType || (LockType = {}));
// -----------------------------------------------------------------------------
// Private
// -----------------------------------------------------------------------------
// Base path for all files endpoints
const BASE_PATH = '/files', VERSIONS_SUBRESOURCE = '/versions', WATERMARK_SUBRESOURCE = '/watermark', UPLOAD_SESSION_SUBRESOURCE = '/upload_sessions', ZIP_DOWNLOAD_PATH = '/zip_downloads';
/**
* Returns the multipart form value for file upload metadata.
* @param {string} parentFolderID - the ID of the parent folder to upload to
* @param {string} filename - the file name that the uploaded file should have
* @param {Object} [options] - Optional metadata
* @returns {Object} - the form value expected by the API for the 'metadata' key
* @private
*/
function createFileMetadataFormData(parentFolderID, filename, options) {
// Although the filename and parent folder ID can be specified without using a
// metadata form field, Platform has recommended that we use the metadata form
// field to specify these parameters (one benefit is that UTF-8 characters can
// be specified in the filename).
var metadata = {
name: filename,
parent: { id: parentFolderID },
};
Object.assign(metadata, options);
return JSON.stringify(metadata);
}
/**
* Returns the multipart form value for file upload content.
* @param {string|Buffer|Stream} content - the content of the file being uploaded
* @param {Object} options - options for the content
* @returns {Object} - the form value expected by the API for the 'content' key
* @private
*/
function createFileContentFormData(content, options) {
// The upload API appears to look for a form field that contains a filename
// property and assume that this form field contains the file content. Thus,
// the value of name does not actually matter (as long as it does not conflict
// with other field names). Similarly, the value of options.filename does not
// matter either (as long as it exists), since the upload API will use the
// filename specified in the metadata form field instead.
return {
value: content,
options: Object.assign({ filename: 'unused' }, options),
};
}
/**
* Poll the representation info URL until representation is generated,
* then return content URL template.
* @param {BoxClient} client The client to use for making API calls
* @param {string} infoURL The URL to use for getting representation info
* @returns {Promise<string>} A promise resolving to the content URL template
*/
function pollRepresentationInfo(client, infoURL) {
return client.get(infoURL).then((response /* FIXME */) => {
if (response.statusCode !== 200) {
throw errors_1.default.buildUnexpectedResponseError(response);
}
var info = response.body;
switch (info.status.state) {
case 'success':
case 'viewable':
case 'error':
return info;
case 'none':
case 'pending':
return bluebird_1.default.delay(1000).then(() => pollRepresentationInfo(client, infoURL));
default:
throw new Error(`Unknown representation status: ${info.status.state}`);
}
});
}
// ------------------------------------------------------------------------------
// Public
// ------------------------------------------------------------------------------
/**
* Simple manager for interacting with all 'File' endpoints and actions.
*
* @param {BoxClient} client The Box API Client that is responsible for making calls to the API
* @constructor
*/
class Files {
constructor(client) {
// Attach the client, for making API calls
this.client = client;
}
/**
* Requests a file object with the given ID.
*
* API Endpoint: '/files/:fileID'
* Method: GET
*
* @param {string} fileID - Box ID of the file being requested
* @param {Object} [options] - Additional options for the request. Can be left null in most cases.
* @param {Function} [callback] - Passed the file information if it was acquired successfully
* @returns {Promise<Object>} A promise resolving to the file object
*/
get(fileID, options, callback) {
var params = {
qs: options,
};
var apiPath = (0, url_path_1.default)(BASE_PATH, fileID);
return this.client.wrapWithDefaultHandler(this.client.get)(apiPath, params, callback);
}
/**
* Requests a download URL for a given file.
*
* API Endpoint: '/files/:fileID/content'
* Method: GET
* Special Expected Responses:
* 202 ACCEPTED - Download isn't available yet. Returns an error.
* 302 FOUND - Download is available. A Download URL is returned.
*
* @param {string} fileID - Box ID of the file being requested
* @param {Object} [options] - Additional options for the request. Can be left null in most cases.
* @param {Function} [callback] - Passed the download URL if request was successful.
* @returns {Promise<string>} A promise resolving to the file's download URL
*/
getDownloadURL(fileID, options, callback) {
var params = {
qs: options,
};
var apiPath = (0, url_path_1.default)(BASE_PATH, fileID, '/content');
// Handle Special API Response
return this.client
.get(apiPath, params)
.then((response /* FIXME */) => {
switch (response.statusCode) {
// 302 - Found
// No data returned, but the location header points to a download link for that file.
case http_status_1.default.FOUND:
return response.headers.location;
// 202 - Download isn't ready yet.
case http_status_1.default.ACCEPTED:
throw errors_1.default.buildResponseError(response, 'Download not ready at this time');
// Unexpected Response
default:
throw errors_1.default.buildUnexpectedResponseError(response);
}
})
.asCallback(callback);
}
/**
* Requests a Readable Stream for the given file ID.
*
* API Endpoint: '/files/:fileID/content'
* Method: GET
* Special Expected Responses:
* 202 ACCEPTED - Download isn't available yet. Returns an error.
* 302 FOUND - Download is available. A Download stream is returned.
*
* @param {string} fileID - Box ID of the file being requested
* @param {Object} [options] - Additional options for the request. Can be left null in most cases.
* @param {string} [options.version] - ID of the version of this file to download
* @param {int[]} [options.byteRange] - starting and ending bytes of the file to read, e.g. [0, 99] to read the first 100 bytes
* @param {Function} [callback] - passed the readable stream if request was successful
* @returns {Promise<Readable>} A promise resolving for the file stream
*/
getReadStream(fileID, options, callback) {
options = options || {};
var downloadStreamOptions = {
streaming: true,
headers: {},
};
if (options.byteRange) {
var range = options.byteRange;
delete options.byteRange;
downloadStreamOptions.headers.Range = `bytes=${range[0]}-${range[1]}`;
}
// Get the download URL to download from
return (this.getDownloadURL(fileID, options)
// Return a read stream to download the file
.then((url) => this.client.get(url, downloadStreamOptions))
.asCallback(callback));
}
/**
* Gets the comments on a file.
*
* API Endpoint: '/files/:fileID/comments'
* Method: GET
*
* @param {string} fileID - Box file id of the file
* @param {Object} [options] - Additional options for the request. Can be left null in most cases.
* @param {Function} [callback] - passed the file comments if they were successfully acquired
* @returns {Promise<Object>} A promise resolving to the collection of comments
*/
getComments(fileID, options, callback) {
var params = {
qs: options,
};
var apiPath = (0, url_path_1.default)(BASE_PATH, fileID, '/comments');
return this.client.wrapWithDefaultHandler(this.client.get)(apiPath, params, callback);
}
/**
* Update some information about a given file.
*
* API Endpoint: '/files/:fileID'
* Method: PUT
*
* @param {string} fileID - Box ID of the file being requested
* @param {Object} updates - File fields to update
* @param {string} [updates.etag] Only apply the updates if the file etag matches
* @param {string} [updates.fields] Comma-separated list of fields to return
* @param {Function} [callback] - Passed the updated file information if it was acquired successfully
* @returns {Promise<Object>} A promise resolving to the update file object
*/
update(fileID, updates, callback) {
var params = {
body: updates,
};
if (updates && updates.etag) {
params.headers = {
'If-Match': updates.etag,
};
delete updates.etag;
}
if (updates && updates.fields) {
params.qs = {
fields: updates.fields,
};
delete updates.fields;
}
var apiPath = (0, url_path_1.default)(BASE_PATH, fileID);
return this.client.wrapWithDefaultHandler(this.client.put)(apiPath, params, callback);
}
/**
* Add a file to a given collection
*
* API Endpoint: '/files/:fileID'
* Method: PUT
*
* @param {string} fileID - The file to add to the collection
* @param {string} collectionID - The collection to add the file to
* @param {Function} [callback] - Passed the updated file if successful, error otherwise
* @returns {Promise<Object>} A promise resolving to the updated file object
*/
addToCollection(fileID, collectionID, callback) {
return this.get(fileID, { fields: 'collections' })
.then((data) => {
var collections = data.collections || [];
// Convert to correct format
collections = collections.map((c /* FIXME */) => ({
id: c.id,
}));
if (!collections.find((c /* FIXME */) => c.id === collectionID)) {
collections.push({ id: collectionID });
}
return this.update(fileID, { collections });
})
.asCallback(callback);
}
/**
* Remove a file from a given collection
*
* API Endpoint: '/files/:fileID'
* Method: PUT
*
* @param {string} fileID - The file to remove from the collection
* @param {string} collectionID - The collection to remove the file from
* @param {Function} [callback] - Passed the updated file if successful, error otherwise
* @returns {Promise<Object>} A promise resolving to the updated file object
*/
removeFromCollection(fileID, collectionID, callback) {
return this.get(fileID, { fields: 'collections' })
.then((data /* FIXME */) => {
var collections = data.collections || [];
// Convert to correct object format and remove the specified collection
collections = collections
.map((c /* FIXME */) => ({ id: c.id }))
.filter((c /* FIXME */) => c.id !== collectionID);
return this.update(fileID, { collections });
})
.asCallback(callback);
}
/**
* Move a file into a new parent folder.
*
* API Endpoint: '/files/:fileID'
* Method: PUT
*
* @param {string} fileID - The Box ID of the file being requested
* @param {string} newParentID - The Box ID for the new parent folder. '0' to move to All Files.
* @param {Function} [callback] - Passed the updated file information if it was acquired successfully
* @returns {Promise<Object>} A promise resolving to the updated file object
*/
move(fileID, newParentID, callback) {
var params = {
body: {
parent: {
id: newParentID,
},
},
};
var apiPath = (0, url_path_1.default)(BASE_PATH, fileID);
return this.client.wrapWithDefaultHandler(this.client.put)(apiPath, params, callback);
}
/**
* Copy a file into a new folder.
*
* API Endpoint: '/files/:fileID/copy
* Method: POST
*
* @param {string} fileID - The Box ID of the file being requested
* @param {string} newParentID - The Box ID for the new parent folder. '0' to copy to All Files.
* @param {Object} [options] - Optional parameters for the copy operation, can be left null in most cases
* @param {string} [options.name] - A new name to use if there is an identically-named item in the new parent folder
* @param {string} [options.version] - An optional ID of the specific file version to copy
* @param {Function} [callback] - passed the new file info if call was successful
* @returns {Promise<Object>} A promise resolving to the new file object
*/
copy(fileID, newParentID, options, callback) {
// @NOTE(mwiller) 2016-10-25: Shuffle arguments to maintain backward compatibility
// This can be removed at the v2.0 update
if (typeof options === 'function') {
callback = options;
options = {};
}
options = options || {};
options.parent = {
id: newParentID,
};
var params = {
body: options,
};
var apiPath = (0, url_path_1.default)(BASE_PATH, fileID, '/copy');
return this.client.wrapWithDefaultHandler(this.client.post)(apiPath, params, callback);
}
/**
* Delete a given file.
*
* API Endpoint: '/files/:fileID'
* Method: DELETE
*
* @param {string} fileID - Box ID of the file being requested
* @param {Object} [options] Optional parameters
* @param {string} [options.etag] Only delete the file if the etag value matches
* @param {Function} [callback] - Empty response body passed if successful.
* @returns {Promise<void>} A promise resolving to nothing
*/
delete(fileID, options, callback) {
// Switch around arguments if necessary for backwards compatibility
if (typeof options === 'function') {
callback = options;
options = {};
}
var params = {};
if (options && options.etag) {
params.headers = {
'If-Match': options.etag,
};
}
var apiPath = (0, url_path_1.default)(BASE_PATH, fileID);
return this.client.wrapWithDefaultHandler(this.client.del)(apiPath, params, callback);
}
/**
* Get preflight information for a new file upload. Without any file data,
* this will return an upload URL and token to be used when uploading the file.
* Using this upload URL will allow for the fastest upload, and the one-time
* token can be passed to a worker or other client to actually perform the
* upload with. If file data (e.g. size, parent, name) is passed, it will be
* validated as if the actual file were being uploaded. This enables checking
* of preconditions such as name uniqueness and available storage space before
* attempting a large file upload.
*
* API Endpoint: '/files/content'
* Method: OPTIONS
*
* @param {string} parentFolderID - The id of the parent folder to upload to
* @param {Object} [fileData] - Optional data about the file to be uploaded
* @param {Object} [options] - Additional options for the request. Can be left null in most cases.
* @param {Function} [callback] - Called with upload data if successful, or err if the upload would not succeed
* @returns {Promise<Object>} A promise resolving to the upload data
*/
preflightUploadFile(parentFolderID, fileData, options, callback) {
var params = {
body: {
parent: {
id: parentFolderID,
},
},
qs: options,
};
if (fileData) {
Object.assign(params.body, fileData);
}
var apiPath = (0, url_path_1.default)(BASE_PATH, '/content');
return this.client.wrapWithDefaultHandler(this.client.options)(apiPath, params, callback);
}
/**
* Get preflight information for a file version upload. Without any file data,
* this will return an upload URL and token to be used when uploading the file.
* Using this upload URL will allow for the fastest upload, and the one-time
* token can be passed to a worker or other client to actually perform the
* upload with. If file data (e.g. size, parent, name) is passed, it will be
* validated as if the actual file were being uploaded. This enables checking
* of preconditions such as name uniqueness and available storage space before
* attempting a large file upload.
*
* API Endpoint: '/files/:fileID/content'
* Method: OPTIONS
*
* @param {string} fileID - The file ID to which a new version will be uploaded
* @param {Object} [fileData] - Optional data about the file to be uploaded
* @param {Object} [options] - Additional options for the request. Can be left null in most cases.
* @param {Function} [callback] - Called with upload data if successful, or err if the upload would not succeed
* @returns {Promise<Object>} A promise resolving to the upload data
*/
preflightUploadNewFileVersion(fileID, fileData, options, callback) {
var params = {
qs: options,
};
if (fileData) {
params.body = fileData;
}
var apiPath = (0, url_path_1.default)(BASE_PATH, fileID, '/content');
return this.client.wrapWithDefaultHandler(this.client.options)(apiPath, params, callback);
}
/**
* If there are previous versions of this file, this method can be used to promote one of the older
* versions to the top of the stack. This actually mints a copy of the old version and puts it on
* the top of the versions stack. The file will have the exact same contents, the same SHA1/etag,
* and the same name as the original. Other properties such as comments do not get updated to their former values.
*
* API Endpoint: '/files/:fileID/versions/current'
* Method: POST
*
* @param {string} fileID - The file ID which version will be promoted
* @param {string} versionID - The ID of the file_version that you want to make current
* @param {Function} [callback] - Passed the promoted file version information if successful, error otherwise
* @returns {Promise<Object>} A promise resolving to the promoted file version
*/
promoteVersion(fileID, versionID, callback) {
var apiPath = (0, url_path_1.default)(BASE_PATH, fileID, VERSIONS_SUBRESOURCE, '/current'), params = {
body: {
type: 'file_version',
id: versionID,
},
};
return this.client.wrapWithDefaultHandler(this.client.post)(apiPath, params, callback);
}
/**
* Uploads a new file. Unlike non-upload methods, this method will not perform any retries.
* This method currently does not support any optional parameters such as contentModifiedAt.
*
* API Endpoint: '/files/content'
* Method: POST
*
* @param {string} parentFolderID - the id of the parent folder to upload to
* @param {string} filename - the file name that the uploaded file should have
* @param {string|Buffer|ReadStream} content - the content of the file. It can be a string, a Buffer, or a read stream
* (like that returned by fs.createReadStream()).
* @param {Object} [options] - Optional parameters
* @param {string} [options.content_created_at] - RFC 3339 timestamp when the file was created
* @param {string} [options.content_modified_at] - RFC 3339 timestamp when the file was last modified
* @param {int} [options.content_length] - Optional length of the content. Required if content is a read stream of any type other than fs stream.
* @param {string} [options.description] - Optional description of the uploaded file.
* @param {Function} [callback] - called with data about the upload if successful, or an error if the
* upload failed
* @returns {Promise<Object>} A promise resolving to the uploaded file
*/
uploadFile(parentFolderID, filename, content, options, callback) {
// Shuffle around optional parameter
if (typeof options === 'function') {
callback = options;
options = {};
}
var formOptions = {};
if (options && options.hasOwnProperty('content_length')) {
formOptions.knownLength = options.content_length;
// Delete content_length from options so it's not added to the attributes of the form
delete options.content_length;
}
var apiPath = (0, url_path_1.default)(BASE_PATH, '/content'), multipartFormData = {
attributes: createFileMetadataFormData(parentFolderID, filename, options),
content: createFileContentFormData(content, formOptions),
};
return this.client.wrapWithDefaultHandler(this.client.upload)(apiPath, null, multipartFormData, callback);
}
/**
* Uploads a new version of a file. Unlike non-upload methods, this method will not perform any retries.
* This method currently does not support any optional parameters such as contentModifiedAt.
*
* API Endpoint: '/files/:fileID/content'
* Method: POST
*
* @param {string} fileID - the id of the file to upload a new version of
* @param {string|Buffer|Stream} content - the content of the file. It can be a string, a Buffer, or a read stream
* (like that returned by fs.createReadStream()).
* @param {Object} [options] - Optional parameters
* @param {string} [options.content_modified_at] - RFC 3339 timestamp when the file was last modified
* @param {string} [options.name] - A new name for the file
* @param {int} [options.content_length] - Optional length of the content. Required if content is a read stream of any type other than fs stream.
* @param {string} [options.description] - Optional description of the uploaded new file version.
* @param {Function} [callback] - called with data about the upload if successful, or an error if the
* upload failed
* @returns {Promise<Object>} A promise resolving to the uploaded file
*/
uploadNewFileVersion(fileID, content, options, callback) {
// Shuffle around optional parameter
if (typeof options === 'function') {
callback = options;
options = {};
}
var apiPath = (0, url_path_1.default)(BASE_PATH, fileID, '/content'), multipartFormData = {};
var formOptions = {};
if (options) {
if (options.hasOwnProperty('content_length')) {
formOptions.knownLength = options.content_length;
// Delete content_length from options so it's not added to the attributes of the form
delete options.content_length;
}
multipartFormData.attributes = JSON.stringify(options);
}
multipartFormData.content = createFileContentFormData(content, formOptions);
return this.client.wrapWithDefaultHandler(this.client.upload)(apiPath, null, multipartFormData, callback);
}
/**
* Retrieves all metadata associated with a file.
*
* API Endpoint: '/files/:fileID/metadata'
* Method: GET
*
* @param {string} fileID - the ID of the file to get metadata for
* @param {Function} [callback] - called with an array of metadata when successful
* @returns {Promise<Object>} A promise resolving to a collection of metadata on the file
*/
getAllMetadata(fileID, callback) {
var apiPath = (0, url_path_1.default)(BASE_PATH, fileID, 'metadata');
return this.client.wrapWithDefaultHandler(this.client.get)(apiPath, null, callback);
}
/**
* Retrieve a single metadata template instance for a file.
*
* API Endpoint: '/files/:fileID/metadata/:scope/:template'
* Method: GET
*
* @param {string} fileID - The ID of the file to retrive the metadata of
* @param {string} scope - The scope of the metadata template, e.g. "global"
* @param {string} template - The metadata template to retrieve
* @param {Function} [callback] - Passed the metadata template if successful
* @returns {Promise<Object>} A promise resolving to the metadata template
*/
getMetadata(fileID, scope, template, callback) {
var apiPath = (0, url_path_1.default)(BASE_PATH, fileID, 'metadata', scope, template);
return this.client.wrapWithDefaultHandler(this.client.get)(apiPath, null, callback);
}
/**
* Adds metadata to a file. Metadata must either match a template schema or
* be placed into the unstructured "properties" template in global scope.
*
* API Endpoint: '/files/:fileID/metadata/:scope/:template'
* Method: POST
*
* @param {string} fileID - The ID of the file to add metadata to
* @param {string} scope - The scope of the metadata template, e.g. "enterprise"
* @param {string} template - The metadata template schema to add
* @param {Object} data - Key/value pairs tp add as metadata
* @param {Function} [callback] - Called with error if unsuccessful
* @returns {Promise<Object>} A promise resolving to the new metadata
*/
addMetadata(fileID, scope, template, data, callback) {
var apiPath = (0, url_path_1.default)(BASE_PATH, fileID, 'metadata', scope, template), params = {
body: data,
};
return this.client.wrapWithDefaultHandler(this.client.post)(apiPath, params, callback);
}
/**
* Updates a metadata template instance with JSON Patch-formatted data.
*
* API Endpoint: '/files/:fileID/metadata/:scope/:template'
* Method: PUT
*
* @param {string} fileID - The file to update metadata for
* @param {string} scope - The scope of the template to update
* @param {string} template - The template to update
* @param {Object} patch - The patch data
* @param {Function} [callback] - Called with updated metadata if successful
* @returns {Promise<Object>} A promise resolving to the updated metadata
*/
updateMetadata(fileID, scope, template, patch, callback) {
var apiPath = (0, url_path_1.default)(BASE_PATH, fileID, 'metadata', scope, template), params = {
body: patch,
headers: {
'Content-Type': 'application/json-patch+json',
},
};
return this.client.wrapWithDefaultHandler(this.client.put)(apiPath, params, callback);
}
/**
* Sets metadata on a file, overwriting any metadata that exists for the provided keys.
*
* @param {string} fileID - The file to set metadata on
* @param {string} scope - The scope of the metadata template
* @param {string} template - The key of the metadata template
* @param {Object} metadata - The metadata to set
* @param {Function} [callback] - Called with updated metadata if successful
* @returns {Promise<Object>} A promise resolving to the updated metadata
*/
setMetadata(fileID, scope, template, metadata, callback) {
return this.addMetadata(fileID, scope, template, metadata)
.catch((err /* FIXME */) => {
if (err.statusCode !== 409) {
throw err;
}
// Metadata already exists on the file; update instead
var updates = Object.keys(metadata).map((key) => ({
op: 'add',
path: `/${key}`,
value: metadata[key],
}));
return this.updateMetadata(fileID, scope, template, updates);
})
.asCallback(callback);
}
/**
* Deletes a metadata template from a file.
*
* API Endpoint: '/files/:fileID/metadata/:scope/:template'
* Method: DELETE
*
* @param {string} fileID - The ID of the file to remove metadata from
* @param {string} scope - The scope of the metadata template
* @param {string} template - The template to remove from the file
* @param {Function} [callback] - Called with nothing if successful, error otherwise
* @returns {Promise<void>} A promise resolving to nothing
*/
deleteMetadata(fileID, scope, template, callback) {
var apiPath = (0, url_path_1.default)(BASE_PATH, fileID, 'metadata', scope, template);
return this.client.wrapWithDefaultHandler(this.client.del)(apiPath, null, callback);
}
/**
* Permanently deletes an item that is in the trash. The item will no longer exist in Box. This action cannot be undone.
*
* API Endpoint: '/files/:fileID/trash'
* Method: DELETE
*
* @param {string} fileID - The ID of the file to remove metadata from
* @param {Object} [options] Optional parameters
* @param {string} [options.etag] Only delete the file if the etag matches
* @param {Function} [callback] - Called with nothing if successful, error otherwise
* @returns {Promise<void>} A promise resolving to nothing
*/
deletePermanently(fileID, options, callback) {
if (typeof options === 'function') {
callback = options;
options = {};
}
var params = {};
if (options && options.etag) {
params.headers = {
'If-Match': options.etag,
};
}
var apiPath = (0, url_path_1.default)(BASE_PATH, fileID, '/trash');
return this.client.wrapWithDefaultHandler(this.client.del)(apiPath, params, callback);
}
/**
* Retrieves a file that has been moved to the trash.
*
* API Endpoint: '/files/:fileID/trash'
* Method: GET
*
* @param {string} fileID - The ID of the file being requested
* @param {Object} [options] - Additional options for the request. Can be left null in most cases.
* @param {Function} [callback] - Passed the trashed file information if successful, error otherwise
* @returns {Promise<Object>} A promise resolving to the trashed file
*/
getTrashedFile(fileID, options, callback) {
var params = {
qs: options,
};
var apiPath = (0, url_path_1.default)(BASE_PATH, fileID, 'trash');
return this.client.wrapWithDefaultHandler(this.client.get)(apiPath, params, callback);
}
/**
* Retrieves all of the tasks for given file.
*
* API Endpoint: '/files/:fileID/tasks'
* Method: GET
*
* @param {string} fileID - The ID of the file to get tasks for
* @param {Object} [options] - Additional options for the request. Can be left null in most cases.
* @param {Function} [callback] - Passed the file tasks if successful, error otherwise
* @returns {Promise<Object>} A promise resolving to a collections of tasks on the file
*/
getTasks(fileID, options, callback) {
var params = {
qs: options,
};
var apiPath = (0, url_path_1.default)(BASE_PATH, fileID, '/tasks');
return this.client.wrapWithDefaultHandler(this.client.get)(apiPath, params, callback);
}
/**
* Used to retrieve an expiring URL for creating an embedded preview session.
* The URL will expire after 60 seconds and the preview session will expire after 60 minutes.
*
* API Endpoint: '/files/:fileID?fields=expiring_embed_link'
* Method: GET
*
* @param {string} fileID - The ID of the file to generate embed link for
* @param {Function} [callback] - Passed with the embed link if successful, error otherwise
* @returns {Promise<string>} A promise resolving to the file embed link URL
*/
getEmbedLink(fileID, callback) {
var params = {
qs: {
fields: 'expiring_embed_link',
},
};
var apiPath = (0, url_path_1.default)(BASE_PATH, fileID);
return this.client
.get(apiPath, params)
.then((response /* FIXME */) => {
if (response.statusCode !== http_status_1.default.OK) {
throw errors_1.default.buildUnexpectedResponseError(response);
}
return response.body.expiring_embed_link.url;
})
.asCallback(callback);
}
/**
* Locks a file.
*
* API Endpoint: '/files/:fileID'
* Method: PUT
*
* @param {string} fileID - The ID of the file to lock
* @param {Object} [options] - Optional parameters, can be left null in most cases
* @param {?string} [options.expires_at] - The time the lock expires
* @param {boolean} [options.is_download_prevented] - Whether or not the file can be downloaded while locked
* @param {Function} [callback] - Passed with the locked file information if successful, error otherwise
* @returns {Promise<Object>} A promise resolving to the locked file object
*/
lock(fileID, options, callback) {
var apiPath = (0, url_path_1.default)(BASE_PATH, fileID), params = {
body: {
lock: {
type: LockType.LOCK,
},
},
};
Object.assign(params.body.lock, options);
return this.client.wrapWithDefaultHandler(this.client.put)(apiPath, params, callback);
}
/**
* Unlocks a file.
*
* API Endpoint: '/files/:fileID'
* Method: PUT
*
* @param {string} fileID - The ID of the file to unlock
* @param {Function} [callback] - Passed with the unlocked file information if successful, error otherwise
* @returns {Promise<Object>} A promise resolving to the unlocked file object
*/
unlock(fileID, callback) {
var apiPath = (0, url_path_1.default)(BASE_PATH, fileID), params = {
body: {
lock: null,
},
};
return this.client.wrapWithDefaultHandler(this.client.put)(apiPath, params, callback);
}
/**
* Restores an item that has been moved to the trash. Default behavior is to
* restore the item to the folder it was in before it was moved to the trash.
* If that parent folder no longer exists or if there is now an item with the
* same name in that parent folder, the new parent folder and/or new name will
* need to be included in the request.
*
* API Endpoint: '/files/:fileID'
* Method: POST
*
* @param {string} fileID - The ID of the file to restore
* @param {Object} [options] - Optional parameters, can be left null in most cases
* @param {string} [options.name] - The new name for this item
* @param {string} [options.parent_id] - The new parent folder for this item
* @param {Function} [callback] - Called with item information if successful, error otherwise
* @returns {Promise<Object>} A promise resolving to the restored file object
*/
restoreFromTrash(fileID, options, callback) {
// Set up the parent_id parameter
if (options && options.parent_id) {
options.parent = {
id: options.parent_id,
};
delete options.parent_id;
}
var apiPath = (0, url_path_1.default)(BASE_PATH, fileID), params = {
body: options || {},
};
return this.client.wrapWithDefaultHandler(this.client.post)(apiPath, params, callback);
}
/**
* If there are previous versions of this file, this method can be used to retrieve information
* about the older versions.
*
* API Endpoint: '/files/:fileID/versions'
* Method: GET
*
* @param {string} fileID - The ID of the file to view version for
* @param {Object} [options] - Additional options for the request. Can be left null in most cases.
* @param {Function} [callback] - Passed a list of previous file versions if successful, error otherwise
* @returns {Promise<Object>} A promise resolving to the collection of file versions
*/
getVersions(fileID, options, callback) {
var apiPath = (0, url_path_1.default)(BASE_PATH, fileID, VERSIONS_SUBRESOURCE), params = {
qs: options,
};
return this.client.wrapWithDefaultHandler(this.client.get)(apiPath, params, callback);
}
/**
* Used to retrieve the watermark for a corresponding Box file.
*
* API Endpoint: '/files/:fileID/watermark'
* Method: GET
*
* @param {string} fileID - The Box ID of the file to get watermark for
* @param {Object} [options] - Additional options for the request. Can be left null in most cases.
* @param {Function} [callback] - Passed the watermark information if successful, error otherwise
* @returns {Promise<Object>} A promise resolving to the watermark info
*/
getWatermark(fileID, options, callback) {
var apiPath = (0, url_path_1.default)(BASE_PATH, fileID, WATERMARK_SUBRESOURCE), params = {
qs: options,
};
return this.client
.get(apiPath, params)
.then((response /* FIXME */) => {
if (response.statusCode !== 200) {
throw errors_1.default.buildUnexpectedResponseError(response);
}
return response.body.watermark;
})
.asCallback(callback);
}
/**
* Used to apply or update the watermark for a corresponding Box file.
*
* API Endpoint: '/files/:fileID/watermark'
* Method: PUT
*
* @param {string} fileID - The Box ID of the file to update watermark for
* @param {Object} [options] - Optional parameters, can be left null
* @param {Function} [callback] - Passed the watermark information if successful, error otherwise
* @returns {Promise<Object>} A promise resolving to the watermark info
*/
applyWatermark(fileID, options, callback) {
var apiPath = (0, url_path_1.default)(BASE_PATH, fileID, WATERMARK_SUBRESOURCE), params = {
body: {
watermark: {
imprint: 'default', // Currently the API only supports default imprint
},
},
};
Object.assign(params.body.watermark, options);
return this.client.wrapWithDefaultHandler(this.client.put)(apiPath, params, callback);
}
/**
* Used to remove the watermark for a corresponding Box file.
*
* API Endpoint: '/files/:fileID/watermark'
* Method: DELETE
*
* @param {string} fileID - The Box ID of the file to remove watermark from
* @param {Function} [callback] - Empty response body passed if successful, error otherwise
* @returns {Promise<void>} A promise resolving to nothing
*/
removeWatermark(fileID, callback) {
var apiPath = (0, url_path_1.default)(BASE_PATH, fileID, WATERMARK_SUBRESOURCE);
return this.client.wrapWithDefaultHandler(this.client.del)(apiPath, null, callback);
}
/**
* Discards a specific file version to the trash. Depending on the enterprise settings
* for this user, the item will either be actually deleted from Box or moved to the trash.
*
* API Endpoint: '/files/:fileID/version/:versionID'
* Method: DELETE
*
* @param {string} fileID - The file ID which old version will be moved to the trash or delete permanently
* @param {string} versionID - The ID of the version to move to the trash or delete permanently
* @param {Object} [options] Optional parameters
* @param {string} [options.etag] Only delete the version of the file etag matches
* @param {Function} [callback] - Empty response body, error otherwise
* @returns {Promise<void>} A promise resolving to nothing
*/
deleteVersion(fileID, versionID, options, callback) {
// Switch around arguments if necessary for backwwards compatibility
if (typeof options === 'function') {
callback = options;
options = {};
}
var params = {};
if (options && options.etag) {
params.headers = {
'If-Match': options.etag,
};
}
var apiPath = (0, url_path_1.default)(BASE_PATH, fileID, VERSIONS_SUBRESOURCE, versionID);
return this.client.wrapWithDefaultHandler(this.client.del)(apiPath, params, callback);
}
/**
* Creates a session used to upload a new file in chunks.. This will first
* verify that the file can be created and then open a session for uploading
* pieces of the file.
*
* API Endpoint: '/files/upload_sessions'
* Method: POST
*
* @param {string} folderID - The ID of the folder to upload the file to
* @param {int} size - The size of the file that will be uploaded
* @param {string} name - The name of the file to be created
* @param {Function} [callback] - Passed the upload session info if successful
* @returns {Promise<Object>} A promise resolving to the new upload session object
*/
createUploadSession(folderID, size, name, callback) {
var apiURL = this.client._uploadBaseURL +
(0, url_path_1.default)(BASE_PATH, UPLOAD_SESSION_SUBRESOURCE), params = {
body: {
folder_id: folderID,
file_size: size,
file_name: name,
},
};
return this.client.wrapWithDefaultHandler(this.client.post)(apiURL, params, callback);
}
/**
* Creates a session used to upload a new version of a file in chunks. This
* will first verify that the version can be created and then open a session for
* uploading pieces of the file.
*
* API Endpoint: '/files/:fileID/upload_sessions'
* Method: POST
*
* @param {string} fileID - The ID of the file to upload a new version of
* @param {int} size - The size of the file that will be uploaded
* @param {Function} [callback] - Passed the upload session info if successful
* @returns {Promise<Object>} A promise resolving to the new upload session object
*/
createNewVersionUploadSession(fileID, size, callback) {
var apiURL = this.client._uploadBaseURL +
(0, url_path_1.default)(BASE_PATH, fileID, UPLOAD_SESSION_SUBRESOURCE), params = {
body: {
file_size: size,
},
};
return this.client.wrapWithDefaultHandler(this.client.post)(apiURL, params, callback);
}
/**
* Uploads a chunk of a file to an open upload session
*
* API Endpoint: '/files/upload_sessions/:sessionID'
* Method: PUT
*
* @param {string} sessionID - The ID of the upload session to upload to
* @param {Buffer|string} part - The chunk of the file to upload
* @param {int} offset - The byte position where the chunk begins in the file
* @param {int} totalSize - The total size of the file being uploaded
* @param {Function} [callback] - Passed the part definition if successful
* @returns {Promise<Object>} A promise resolving to the part object
*/
uploadPart(sessionID, part, offset, totalSize, callback) {
var apiURL = this.client._uploadBaseURL +
(0, url_path_1.default)(BASE_PATH, UPLOAD_SESSION_SUBRESOURCE, sessionID);
var hash = crypto_1.default.createHash('sha1').update(part).digest('base64');
var params = {
headers: {
'Content-Type': 'application/octet-stream',
Digest: `SHA=${hash}`,
'Content-Range': `bytes ${offset}-${offset + part.length - 1}/${totalSize}`,
},
json: false,
body: part,
};
return this.client
.put(apiURL, params)
.then((response /* FIXME */) => {
if (response.statusCode !== 200) {
throw errors_1.default.buildUnexpectedResponseError(response);
}
return JSON.parse(response.body);
})
.asCallback(callback);
}
/**
* Commit an upload session after all parts have been uploaded, creating the new file
*
* API Endpoint: '/files/upload_sessions/:sessionID/commit'
* Method: POST
*
* @param {string} sessionID - The ID of the upload session to commit
* @param {string} fileHash - The base64-encoded SHA-1 hash of the file being uploaded
* @param {Object} [options] - Optional parameters set on the created file, can be left null
* @param {UploadPart[]} [options.parts] The list of uploaded parts to be committed, will be fetched from the API otherwise
* @param {string} [options.description] - Optional description of the uploaded file.
* @param {Function} [callback] - Passed the new file information if successful
* @returns {Promise<Object>} A promise resolving to the uploaded file object
*/
commitUploadSession(sessionID, fileHash, options, callback) {
options = options || {};
var userParts;
if (options.parts) {
userParts = options.parts;
delete options.parts;
}
var apiURL = this.client._uploadBaseURL +
(0, url_path_1.default)(BASE_PATH, UPLOAD_SESSION_SUBRESOURCE, sessionID, 'commit'), params = {
headers: {
Digest: `SHA=${fileHash}`,
},
body: {
attributes: options,
},
};
var fetchParts = (offset /* FIXME */, fetchedParts /* FIXME */) => {
let pagingOptions = {
limit: 1000,
offset,
};
return this.getUploadSessionParts(sessionID, pagingOptions).then((data /* FIXME */) => {
fetchedParts = fetchedParts.concat(data.entries);
if (data.offset + data.entries.length >= data.total_count) {
return bluebird_1.default.resolve(fetchedParts);
}
return fetchParts(offset + data.limit, fetchedParts);
});
};
return (userParts ? bluebird_1.default.resolve(userParts) : fetchParts(0, []))
.then((parts /* FIXME */) => {
// Commit the upload with the list of parts
params.body.parts = parts;
return this.client.post(apiURL, params);
})
.then((response /* FIXME */) => {
if (response.statusCode === 201) {
return response.body;
}
if (response.statusCode === 202) {
var retryInterval = response.headers['retry-after'] || 1;
return bluebird_1.default.delay(retryInterval * 1000).then(() => {
// Ensure we don't have to fetch parts from the API again on retry
options = Object.assign({}, options, {
parts: params.body.parts,
});
return this.commitUploadSession(sessionID, fileHash, options);
});
}
throw errors_1.default.buildUnexpectedResponseError(response);
})
.asCallback(callback);
}
/**
* Abort an upload session, discarding any chunks that were uploaded to it
*
* API Endpoint