Source: box-client.ts

/**
 * @fileoverview Box API Client
 */

import { Promise } from 'bluebird';
// ------------------------------------------------------------------------------
// API Resource Managers
// ------------------------------------------------------------------------------
import CollaborationAllowlist from './managers/collaboration-allowlist';
import Collaborations from './managers/collaborations';
import Collections from './managers/collections';
import Comments from './managers/comments';
import DevicePins from './managers/device-pins';
import Enterprise from './managers/enterprise';
import Events from './managers/events';
import Files from './managers/files';
import Folders from './managers/folders';
import Groups from './managers/groups';
import LegalHoldPolicies from './managers/legal-hold-policies';
import Metadata from './managers/metadata';
import RecentItems from './managers/recent-items';
import RetentionPolicies from './managers/retention-policies';
import Search from './managers/search';
import SharedItems from './managers/shared-items';
import SignRequests from './managers/sign-requests.generated';
import SignTemplates from './managers/sign-templates.generated';
import StoragePolicies from './managers/storage-policies';
import Tasks from './managers/tasks';
import TermsOfService from './managers/terms-of-service';
import Trash from './managers/trash';
import Users from './managers/users';
import WebLinks from './managers/web-links';
import Webhooks from './managers/webhooks';
import FileRequestsManager from "./managers/file-requests-manager";
import ShieldInformationBarriers from "./managers/shield-information-barriers.generated";
import ShieldInformationBarrierSegments from "./managers/shield-information-barrier-segments.generated";
import ShieldInformationBarrierSegmentMembers from "./managers/shield-information-barrier-segment-members.generated";
import ShieldInformationBarrierSegmentRestrictions from "./managers/shield-information-barrier-segment-restrictions.generated";
import ShieldInformationBarrierReports from "./managers/shield-information-barrier-reports.generated";
import IntegrationMappings from "./managers/integration-mappings";

// ------------------------------------------------------------------------------
// Typedefs and Callbacks
// ------------------------------------------------------------------------------

/**
 * A collaboration role constant
 * @typedef {string} CollaborationRole
 */
type CollaborationRole = string;

/**
 * A Box file or folder type constant
 * @typedef {string} ItemType
 */
type ItemType = 'file' | 'folder';

/**
 * An access level constant. Used for setting and updating shared links, folder upload, etc.
 * @typedef {?Object} AccessLevel
 */
type AccessLevel = object | null /* FIXME */;

type APISession = any /* FIXME */;
type APIRequestManager = any /* FIXME */;

// ------------------------------------------------------------------------------
// Requirements
// ------------------------------------------------------------------------------
var util = require('util'),
	qs = require('querystring'),
	errors = require('./util/errors'),
	httpStatusCodes = require('http-status'),
	isIP = require('net').isIP,
	merge = require('merge-options'),
	PagingIterator = require('./util/paging-iterator'),
	pkg = require('../package.json');

// ------------------------------------------------------------------------------
// Private
// ------------------------------------------------------------------------------

// The Authorization header label
var HEADER_AUTHORIZATION = 'Authorization',
	// Prefix our token with this string in the Authorization header
	HEADER_AUTHORIZATION_PREFIX = 'Bearer ',
	// The 'BoxApi' header label
	HEADER_BOXAPI = 'BoxApi',
	// The XFF header label - Used to give the API better information for uploads, rate-limiting, etc.
	HEADER_XFF = 'X-Forwarded-For',
	// As-User header
	HEADER_AS_USER = 'As-User',
	// Range of SUCCESS http status codes
	HTTP_STATUS_CODE_SUCCESS_BLOCK_RANGE = [200, 299];

/**
 * Build the 'Authorization' Header for the API
 *
 * @param {string} accessToken An OAuth Access Token
 * @returns {string} A properly formatted 'Authorization' header
 * @private
 */
function buildAuthorizationHeader(accessToken: string) {
	return HEADER_AUTHORIZATION_PREFIX + accessToken;
}

/**
 * Returns true iff the response is a 401 UNAUTHORIZED that is caused by an expired access token.
 * @param {APIRequest~ResponseObject} response - The response returned by an APIRequestManager request
 * @returns {boolean} - true iff the response is a 401 UNAUTHORIZED caused by an expired access token
 * @private
 */
function isUnauthorizedDueToExpiredAccessToken(response: any /* FIXME */) {
	// There are three cases to consider:
	// 1) The response body is a Buffer. This indicates that the request was malformed (i.e. malformed url) so return false.
	// 2) The status code is UNAUTHORIZED and the response body is an empty object or null. This indicates that the access tokens are expired, so return true.
	// 3) The status code is UNAUTHORIZED and the response body is a non-empty object. This indicates that the 401 was returned for some reason other
	//    than expired tokens, so return false.

	if (Buffer.isBuffer(response.body)) {
		return false;
	}

	var isResponseStatusCodeUnauthorized =
			response.statusCode === httpStatusCodes.UNAUTHORIZED,
		isResponseBodyEmpty =
			!response.body || Object.getOwnPropertyNames(response.body).length === 0;
	return isResponseStatusCodeUnauthorized && isResponseBodyEmpty;
}

/**
 * Returns a full URL. If the url argument begins with http:// or https://, then url is simply returned.
 * Otherwise, the defaultBasePath is prepended to url and returned.
 *
 * @param {string} defaultBasePath The default root URL that will be prepended if `url` is a partial url
 * @param {string} url A full or partial URL that will be used to construct the final URL
 * @returns {string} The final URL
 * @private
 */
function getFullURL(defaultBasePath: string, url: string) {
	if (/^https?:\/\//.test(url)) {
		return url;
	}
	return defaultBasePath + url;
}

/**
 * Construct the X-Box-UA header to send analytics identifiers
 * @param {Object} [client] Analytics client information
 * @returns {string} The header value
 */
function constructBoxUAHeader(client: any /* FIXME */) {
	var analyticsIdentifiers = {
		agent: `box-node-sdk/${pkg.version}`,
		env: `Node/${process.version.replace('v', '')}`,
	} as Record<string, string>;

	if (client) {
		analyticsIdentifiers.client = `${client.name}/${client.version}`;
	}

	return Object.keys(analyticsIdentifiers)
		.map((k) => `${k}=${analyticsIdentifiers[k]}`)
		.join('; ');
}

class BoxClient {
	_session: APISession;
	_requestManager: APIRequestManager;
	_customHeaders: any;

	_baseURL: any;
	_uploadBaseURL: any;
	_uploadRequestTimeoutMS: any;
	_useIterators: any;
	_analyticsClient: any;
	_tokenOptions: any;

	users: any;
	files: Files;
	fileRequests: FileRequestsManager;
	folders: Folders;
	comments: any;
	collaborations: Collaborations;
	groups: any;
	sharedItems: any;
	metadata: any;
	collections: any;
	events: Events;
	search: any;
	tasks: any;
	trash: any;
	enterprise: any;
	legalHoldPolicies: any;
	weblinks: any;
	retentionPolicies: any;
	devicePins: any;
	webhooks: Webhooks;
	recentItems: any;
	collaborationAllowlist: any;
	termsOfService: any;
	storagePolicies: any;
	signRequests: SignRequests;
	signTemplates: SignTemplates;
	shieldInformationBarriers: ShieldInformationBarriers;
	shieldInformationBarrierSegments: ShieldInformationBarrierSegments;
	shieldInformationBarrierSegmentMembers: ShieldInformationBarrierSegmentMembers;
	shieldInformationBarrierSegmentRestrictions: ShieldInformationBarrierSegmentRestrictions;
	shieldInformationBarrierReports: ShieldInformationBarrierReports;
	integrationMappings: IntegrationMappings;

	/* prototype properties assigned below the class declaration */
	collaborationRoles!: Record<string, CollaborationRole>;
	itemTypes!: Record<string, ItemType>;
	accessLevels!: Record<string, AccessLevel>;
	CURRENT_USER_ID!: string;

	/**
	 * The BoxClient can make API calls on behalf of a valid API Session. It is responsible
	 * for formatting the requests and handling the response. Its goal is to deliver
	 * sensible results to the user.
	 *
	 * @param {APISession} apiSession An initialized API Session, used to get/revoke tokens and handle
	 * unauthorized responses from the API.
	 * @param {Config} config The SDK configuration options
	 * @param {APIRequestManager} requestManager The API Request Manager
	 * @constructor
	 */
	constructor(
		apiSession: APISession,
		config: any /* FIXME */,
		requestManager: APIRequestManager
	) {
		// the API Session used by the client for authentication
		this._session = apiSession;

		// Attach a request manager instance for making requests
		this._requestManager = requestManager;

		// An object of custom headers to apply to every request. Modified via BoxClient.setCustomHeader().
		this._customHeaders = {};
		// Attach the configured properties
		this._baseURL = util.format('%s/%s', config.apiRootURL, config.apiVersion);
		this._uploadBaseURL = util.format(
			'%s/%s',
			config.uploadAPIRootURL,
			config.apiVersion
		);
		this._uploadRequestTimeoutMS = config.uploadRequestTimeoutMS;
		this._useIterators = config.iterators;
		this._analyticsClient = config.analyticsClient;

		// Attach API Resource Managers
		this.users = new Users(this);
		this.files = new Files(this);
		this.fileRequests = new FileRequestsManager(this);
		this.folders = new Folders(this);
		this.comments = new Comments(this);
		this.collaborations = new Collaborations(this);
		this.groups = new Groups(this);
		this.sharedItems = new SharedItems(this);
		this.metadata = new Metadata(this);
		this.collections = new Collections(this);
		this.events = new Events(this);
		this.search = new Search(this);
		this.tasks = new Tasks(this);
		this.trash = new Trash(this);
		this.enterprise = new Enterprise(this);
		this.legalHoldPolicies = new LegalHoldPolicies(this);
		this.weblinks = new WebLinks(this);
		this.retentionPolicies = new RetentionPolicies(this);
		this.devicePins = new DevicePins(this);
		this.webhooks = new Webhooks(this);
		this.recentItems = new RecentItems(this);
		this.collaborationAllowlist = new CollaborationAllowlist(this);
		this.termsOfService = new TermsOfService(this);
		this.storagePolicies = new StoragePolicies(this);
		this.signRequests = new SignRequests(this);
		this.signTemplates = new SignTemplates(this);
		this.shieldInformationBarriers = new ShieldInformationBarriers(this);
		this.shieldInformationBarrierSegments = new ShieldInformationBarrierSegments(this);
		this.shieldInformationBarrierSegmentMembers = new ShieldInformationBarrierSegmentMembers(this);
		this.shieldInformationBarrierSegmentRestrictions = new ShieldInformationBarrierSegmentRestrictions(this);
		this.shieldInformationBarrierReports = new ShieldInformationBarrierReports(this);
		this.integrationMappings = new IntegrationMappings(this);
	}

	/**
	 * Returns an object containing the given headers as well as other headers (like the authorization header and
	 * custom headers) that should be included in a request.
	 * @param {?Object} callerHeaders - headers that the caller wishes to include in the request. This method will not
	 * override these headers with its own. Thus, if all the headers that this method was planning to add are already
	 * specified here, this method will return an object with exactly the same headers.
	 * @param {string} accessToken - the access token that will be used to make the request
	 * @returns {Object} - a new object with the headers needed for the request
	 * @private
	 */
	_createHeadersForRequest(callerHeaders: object | null, accessToken: string) {
		var headers: Record<string, string> = {};

		// 'Authorization' - contains your valid access token for authorization
		headers[HEADER_AUTHORIZATION] = buildAuthorizationHeader(accessToken);

		// We copy our own custom headers (XFF, BoxApi, etc.) before copying over the caller-specified headers so that
		// the caller-specified headers will take precedence.
		Object.assign(headers, this._customHeaders, callerHeaders);

		// Add analytics headers last so they cannot be overwritten
		Object.assign(headers, {
			'X-Box-UA': constructBoxUAHeader(this._analyticsClient),
		});

		return headers;
	}

	/**
	 * Makes an API request to the Box API on behalf of the client. Before executing
	 * the request, it first ensures the user has usable tokens. Will be called again
	 * if the request returns a temporary error. Will propogate error if request returns
	 * a permanent error, or if usable tokens are not available.
	 *
	 * @param {Object} params - Request lib params to configure the request
	 * @param {Function} [callback] - passed response data
	 * @returns {Promise} Promise resolving to the response
	 * @private
	 */
	_makeRequest(params: any /* FIXME */, callback?: Function) {
		var promise = this._session
			.getAccessToken(this._tokenOptions)
			.then((accessToken: string) => {
				params.headers = this._createHeadersForRequest(
					params.headers,
					accessToken
				);

				if (params.streaming) {
					// streaming is specific to the SDK, so delete it from params before continuing
					delete params.streaming;
					var responseStream =
						this._requestManager.makeStreamingRequest(params);
					// Listen to 'response' event, so we can cleanup the token store in case when the request is unauthorized
					// due to expired access token
					responseStream.on('response', (response: any /* FIXME */) => {
						if (isUnauthorizedDueToExpiredAccessToken(response)) {
							var expiredTokensError = errors.buildAuthError(response);

							// Give the session a chance to handle the error (ex: a persistent session will clear the token store)
							if (this._session.handleExpiredTokensError) {
								this._session.handleExpiredTokensError(expiredTokensError);
							}
						}
					});

					return responseStream;
				}

				// Make the request to Box, and perform standard response handling
				return this._requestManager.makeRequest(params);
			});

		return promise
			.then((response: any /* FIXME */) => {
				if (!response.statusCode) {
					// Response is not yet complete, and is just a stream that will return the response later
					// Just return the stream, since it doesn't need further response handling
					return response;
				}

				if (isUnauthorizedDueToExpiredAccessToken(response)) {
					var expiredTokensError = errors.buildAuthError(response);

					// Give the session a chance to handle the error (ex: a persistent session will clear the token store)
					if (this._session.handleExpiredTokensError) {
						return this._session.handleExpiredTokensError(expiredTokensError);
					}

					throw expiredTokensError;
				}

				return response;
			})
			.asCallback(callback);
	}

	/**
	 * Set a custom header. A custom header is applied to every request for the life of the client. To
	 * remove a header, set it's value to null.
	 *
	 * @param {string} header The name of the custom header to set.
	 * @param {*} value The value of the custom header. Set to null to remove the given header.
	 * @returns {void}
	 */
	setCustomHeader(header: string, value: any) {
		if (value) {
			this._customHeaders[header] = value;
		} else {
			delete this._customHeaders[header];
		}
	}

	/**
	 * Sets the list of requesting IP addresses for the X-Forwarded-For header. Used to give the API
	 * better information for uploads, rate-limiting, etc.
	 *
	 * @param {string[]} ips - Array of IP Addresses
	 * @returns {void}
	 */
	setIPs(ips: string[]) {
		var validIPs = ips.filter((ipString: string) => isIP(ipString)).join(', ');

		this.setCustomHeader(HEADER_XFF, validIPs);

		this._tokenOptions = { ip: validIPs };
	}

	/**
	 * Sets the shared item context on the API Session. Overwrites any current context.
	 *
	 * @param {string} url The shared link url
	 * @param {?string} password The shared link password, null if no password exists.
	 * @returns {void}
	 */
	setSharedContext(url: string, password: string | null) {
		var sharedContextAuthHeader = this.buildSharedItemAuthHeader(url, password);
		this.setCustomHeader(HEADER_BOXAPI, sharedContextAuthHeader);
	}

	/**
	 * Removes any current shared item context from API Session.
	 *
	 * @returns {void}
	 */
	revokeSharedContext() {
		this.setCustomHeader(HEADER_BOXAPI, null);
	}

	/**
	 * Set up the As-User context, which is used by enterprise admins to
	 * impersonate their managed users and perform actions on their behalf.
	 *
	 * @param {string} userID - The ID of the user to impersonate
	 * @returns {void}
	 */
	asUser(userID: string) {
		this.setCustomHeader(HEADER_AS_USER, userID);
	}

	/**
	 * Revoke the As-User context and return to making calls on behalf of the user
	 * who owns the client's access token.
	 *
	 * @returns {void}
	 */
	asSelf() {
		this.setCustomHeader(HEADER_AS_USER, null);
	}

	/**
	 * Revokes the client's access tokens. The client will no longer be tied to a user
	 * and will be unable to make calls to the API, rendering it effectively useless.
	 *
	 * @param {Function} [callback] Called after revoking, with an error if one existed
	 * @returns {Promise} A promise resolving when the client's access token is revoked
	 */
	revokeTokens(callback: Function) {
		return this._session.revokeTokens(this._tokenOptions).asCallback(callback);
	}

	/**
	 * Exchange the client access token for one with lower scope
	 * @param {string|string[]} scopes The scope(s) requested for the new token
	 * @param {string} [resource] The absolute URL of an API resource to scope the new token to
	 * @param {Object} [options] - Optional parameters
	 * @param {ActorParams} [options.actor] - Optional actor parameters for creating annotator tokens with Token Auth client
	 * @param {SharedLinkParams} [options.sharedLink] - Optional shared link parameters for creating tokens using shared links
	 * @param {Function} [callback] Called with the new token
	 * @returns {Promise<TokenInfo>} A promise resolving to the exchanged token info
	 */
	exchangeToken(
		scopes: string | string[],
		resource?: string,
		options?: Function | object,
		callback?: Function
	) {
		// Shuffle optional parameters
		if (typeof options === 'function') {
			callback = options;
			options = {};
		}

		var opts = Object.assign(
			{ tokenRequestOptions: this._tokenOptions || null },
			options
		);

		return this._session
			.exchangeToken(scopes, resource, opts)
			.asCallback(callback);
	}

	/**
	 * Makes GET request to Box API V2 endpoint
	 *
	 * @param {string} path - path to a certain API endpoint (ex: /file)
	 * @param {?Object} params - object containing parameters for the request, such as query strings and headers
	 * @param {Function} [callback] - passed final API response or err if request failed
	 * @returns {void}
	 */
	get(path: string, params?: object | null, callback?: Function) {
		var newParams = merge({}, params || {});
		newParams.method = 'GET';
		newParams.url = getFullURL(this._baseURL, path);

		return this._makeRequest(newParams, callback);
	}

	/**
	 * Makes POST request to Box API V2 endpoint
	 *
	 * @param {string} path - path to a certain API endpoint (ex: /file)
	 * @param {?Object} params - object containing parameters for the request, such as query strings and headers
	 * @param {Function} [callback] - passed final API response or err if request failed
	 * @returns {void}
	 */
	post(path: string, params: object | null, callback?: Function) {
		var newParams = merge({}, params || {});
		newParams.method = 'POST';
		newParams.url = getFullURL(this._baseURL, path);
		return this._makeRequest(newParams, callback);
	}

	/**
	 * Makes PUT request to Box API V2 endpoint
	 *
	 * @param {string} path - path to a certain API endpoint (ex: /file)
	 * @param {?Object} params - object containing parameters for the request, such as query strings and headers
	 * @param {Function} callback - passed final API response or err if request failed
	 * @returns {void}
	 */
	put(path: string, params?: object | null, callback?: Function) {
		var newParams = merge({}, params || {});
		newParams.method = 'PUT';
		newParams.url = getFullURL(this._baseURL, path);
		return this._makeRequest(newParams, callback);
	}

	/**
	 * Makes DELETE request to Box API V2 endpoint
	 *
	 * @param {string} path - path to a certain API endpoint (ex: /file)
	 * @param {?Object} params - object containing parameters for the request, such as query strings and headers
	 * @param {Function} callback - passed final API response or err if request failed
	 * @returns {void}
	 */
	del(path: string, params: object | null, callback?: Function) {
		var newParams = merge({}, params || {});
		newParams.method = 'DELETE';
		newParams.url = getFullURL(this._baseURL, path);
		return this._makeRequest(newParams, callback);
	}

	/**
	 * Makes an OPTIONS call to a Box API V2 endpoint
	 *
	 * @param {string} path - Path to an API endpoint (e.g. /files/content)
	 * @param {?Object} params - An optional object containing request parameters
	 * @param {Function} callback - Called with API call results, or err if call failed
	 * @returns {void}
	 */
	options(path: string, params: object | null, callback?: Function) {
		var newParams = merge({}, params || {});
		newParams.method = 'OPTIONS';
		newParams.url = getFullURL(this._baseURL, path);

		return this._makeRequest(newParams, callback);
	}

	/**
	 * Makes a POST call to a Box API V2 upload endpoint
	 * @param {string} path - path to an upload API endpoint
	 * @param {?Object} params - an optional object containing request parameters
	 * @param {?Object} formData - multipart form data to include in the upload request {@see https://github.com/mikeal/request#multipartform-data-multipart-form-uploads}
	 * @param {Function} callback - called with API call results, or an error if the call failed
	 * @returns {void}
	 */
	upload(
		path: string,
		params: object | null,
		formData: object | null,
		callback: Function
	) {
		var defaults = {
			method: 'POST',
		};
		var newParams = merge(defaults, params || {});
		newParams.url = getFullURL(this._uploadBaseURL, path);
		newParams.formData = formData;
		newParams.timeout = this._uploadRequestTimeoutMS;

		return this._makeRequest(newParams, callback);
	}

	/**
	 * Build the 'BoxApi' Header used for authenticating access to a shared item
	 *
	 * @param {string} url The shared link url
	 * @param {string} [password] The shared link password
	 * @returns {string} A properly formatted 'BoxApi' header
	 */
	buildSharedItemAuthHeader(url: string, password: string | null) {
		var encodedURL = encodeURIComponent(url),
			encodedPassword = encodeURIComponent(password ?? '');

		if (password) {
			return util.format(
				'shared_link=%s&shared_link_password=%s',
				encodedURL,
				encodedPassword
			);
		}

		return util.format('shared_link=%s', encodedURL);
	}

	/**
	 * Return a callback that properly handles a successful response code by passing the response
	 * body to the original callback. Any request error or unsuccessful response codes are propagated
	 * back to the callback as errors. This is the standard behavior of most endpoints.
	 *
	 * @param {Function} callback The original callback given by the consumer
	 * @returns {?Function} A new callback that processes the response before passing it to the callback.
	 */
	defaultResponseHandler(callback: Function) {
		var self = this;

		if (!callback) {
			return null;
		}

		return function (err: any, response: any /* FIXME */) {
			// Error with Request
			if (err) {
				callback(err);
				return;
			}

			// Successful Response
			if (
				response.statusCode >= HTTP_STATUS_CODE_SUCCESS_BLOCK_RANGE[0] &&
				response.statusCode <= HTTP_STATUS_CODE_SUCCESS_BLOCK_RANGE[1]
			) {
				if (self._useIterators && PagingIterator.isIterable(response)) {
					callback(null, new PagingIterator(response, self));
					return;
				}

				callback(null, response.body);
				return;
			}
			// Unexpected Response
			callback(errors.buildUnexpectedResponseError(response));
		};
	}

	/**
	 * Wrap a client method with the default handler for both callback and promise styles
	 * @param {Function} method The client method (e.g. client.get)
	 * @returns {Function}  The wrapped method
	 */
	wrapWithDefaultHandler(method: Function): Function {
		var self = this;
		return function wrappedClientMethod(/* arguments */) {
			// Check if the last argument is a callback
			var lastArg = arguments[arguments.length - 1],
				callback;
			if (typeof lastArg === 'function') {
				callback = self.defaultResponseHandler(lastArg);
				arguments[arguments.length - 1] = callback;
			}

			var ret = method.apply(self, arguments);

			if (ret instanceof Promise) {
				ret = ret.then((response) => {
					if (
						response.statusCode >= HTTP_STATUS_CODE_SUCCESS_BLOCK_RANGE[0] &&
						response.statusCode <= HTTP_STATUS_CODE_SUCCESS_BLOCK_RANGE[1]
					) {
						if (self._useIterators && PagingIterator.isIterable(response)) {
							return new PagingIterator(response, self);
						}

						return response.body;
					}

					throw errors.buildUnexpectedResponseError(response);
				});
			}

			if (callback) {
				// If the callback will handle any errors, don't worry about the promise
				ret.suppressUnhandledRejections();
			}

			return ret;
		};
	}

	/**
	 * Add a SDK plugin. Warning: This will modify the box-client interface and can override existing properties.
	 * @param {string} name Plugin name. Will be accessible via client.<plugin-name>
	 * @param {Function} plugin The SDK plugin to add
	 * @param {Object} [options] Plugin-specific options
	 * @returns {void}
	 * @throws Will throw an error if plugin name matches an existing method on box-client
	 */
	plug(name: string, plugin: Function, options: object) {
		options = options || {};

		if (name in this && typeof (this as any)[name] === 'function') {
			throw new Error(
				'You cannot define a plugin that overrides an existing method on the client'
			);
		}

		// Create plugin and export plugin onto client.
		(this as any)[name] = plugin(this, options);
	}
}

// ------------------------------------------------------------------------------
// Public
// ------------------------------------------------------------------------------

/**
 * Enum of valid collaboration roles
 *
 * @readonly
 * @enum {CollaborationRole}
 */
BoxClient.prototype.collaborationRoles = {
	EDITOR: 'editor',
	VIEWER: 'viewer',
	PREVIEWER: 'previewer',
	UPLOADER: 'uploader',
	PREVIEWER_UPLOADER: 'previewer uploader',
	VIEWER_UPLOADER: 'viewer uploader',
	CO_OWNER: 'co-owner',
	OWNER: 'owner',
};

/**
 * Enum of Box item types
 *
 * @readonly
 * @enum {ItemType}
 */
BoxClient.prototype.itemTypes = {
	FILE: 'file',
	FOLDER: 'folder',
};

/**
 * Enum of valid values for setting different access levels. To be used when
 * creating and editting shared links, upload emails, etc.
 *
 * @readonly
 * @type {AccessLevel}
 */
BoxClient.prototype.accessLevels = {
	OPEN: { access: 'open' },
	COLLABORATORS: { access: 'collaborators' },
	COMPANY: { access: 'company' },
	DEFAULT: {},
	DISABLED: null,
};

/** @const {string} */
BoxClient.prototype.CURRENT_USER_ID = Users.prototype.CURRENT_USER_ID;

// ------------------------------------------------------------------------------
// Public
// ------------------------------------------------------------------------------

/**
 * @module box-node-sdk/lib/box-client
 * @see {@Link BoxClient}
 */
export = BoxClient;