UNPKG

box-view-api

Version:

Box View API, node.js client library

264 lines (231 loc) 8.14 kB
// Defines the details for the RESTful Box View API, // as well as providing some helper methods for creating URIs to resources // and submitting the request. // // Core node module dependencies // var inspect = require('util').inspect; var querystring = require('querystring'); var url = require('url'); var path = require('path'); // 3rd party library dependencies // var request = require('request'); var FormData = require('form-data'); // Internal dependencies // var BoxViewError = require('./error'); var ErrorTypes = BoxViewError.errors; // aliasing for brevity // private helpers // function normaliseQuery(query) { var nq; switch (typeof query) { case 'string': // parse out the querystring into a hash of the query params nq = querystring.parse(query); break; case 'object': // shallow clone the query hash, so we don't end up mutating the original nq = (function(o){ var c = {}; for (var k in o) { c[k] = o[k]; } return c; })(query); break; default: nq = {}; } return nq; } function wrappedCallback(cb) { cb = (typeof cb === 'function') ? cb : function(){}; return function(e, r, b) { var body; if (typeof b === 'object') { body = b; } else if (typeof b === 'string') { try { body = JSON.parse(b); } catch (x) { body = {body: b}; } } else { body = {body: b}; } cb(e, r, body); }; } function validateCommonArgs(resource, options, cb) { if (typeof resource !== 'string') { throw new BoxViewError(ErrorTypes.INVALID_TYPE, 'Supplied resource is not a string: ' + (typeof resource) + ' [' + inspect(resource) + ']'); } if (resource.length <= 0) { throw new BoxViewError(ErrorTypes.INVALID_TYPE, 'Supplied resource "' + resource + '" is too short'); } if (typeof options !== 'object') { throw new BoxViewError(ErrorTypes.INVALID_TYPE, 'Supplied options is not an object: ' + (typeof options) + ' [' + inspect(options) + ']'); } if (!cb) { throw new BoxViewError(ErrorTypes.INVALID_TYPE, 'No callback supplied'); } if (typeof cb !== 'function') { throw new BoxViewError(ErrorTypes.INVALID_TYPE, 'Supplied callback is not a function: '+(typeof cb)+' ['+inspect(cb)+']'); } } function stringFromStream(stream, callback) { var chunks = []; var bodyLen = 0; stream.on('data', function (chunk) { chunks.push(chunk); bodyLen += chunk.length; }); stream.on('end', function () { var body; if (chunks.length && Buffer.isBuffer(chunks[0])) { body = new Buffer(bodyLen); var i = 0; chunks.forEach(function (chunk) { chunk.copy(body, i, 0, chunk.length); i += chunk.length; }); body = body.toString('utf8'); } else if (chunks.length) { body = chunks.join(''); } callback(body); }); } function normaliseAcceptsHeader(original, mime) { original = original || ''; mime = mime || 'application/json'; var split = original.split(','); if (split.indexOf(mime) < 0 ) { split.push(mime); } return split.join(', '); } // Exposed interface // var API = module.exports = { // Base definitions of the RESTful API // base: { protocol: 'https', host: 'view-api.box.com', pathname: '/1' }, constructor: function(apiToken) { if (typeof apiToken !== 'string') { throw new BoxViewError(ErrorTypes.INVALID_TYPE, 'Supplied API Token is not a string: ' + (typeof apiToken) + ' [' + inspect(apiToken) + ']'); } if (apiToken.length <= 0) { throw new BoxViewError(ErrorTypes.INVALID_API_TOKEN, 'Supplied API Token "' + apiToken + '" is too short'); } this.apiToken = apiToken; }, uriString: function(resource, query) { if (typeof resource !== 'string') { throw new BoxViewError(ErrorTypes.INVALID_TYPE, 'Supplied resource is not a string: '+(typeof resource)+' ['+inspect(resource)+']'); } if (resource.length <= 0) { throw new BoxViewError(ErrorTypes.INVALID_TYPE, 'Supplied resource "'+resource+'" is too short'); } return url.format({ protocol: API.base.protocol, host: API.base.host, pathname: path.join(API.base.pathname, resource), query: normaliseQuery(query) }); }, // get(resource, [{query: query, successStream: writeStream},] callback) get: function(resource) { if (arguments.length < 2 || arguments.length > 3) { throw new BoxViewError(ErrorTypes.INVALID_USE, 'Incorrect number of arguments passed to '+__filename+'.get(): '+arguments.length); } var cb = arguments[arguments.length - 1]; var options = (arguments.length === 3) ? arguments[1] : {}; validateCommonArgs(resource, options, cb); var successStream = options.successStream; options.qs = normaliseQuery(options.query); options.headers = options.headers || {}; options.uri = API.uriString(resource); options.headers.Authorization = 'Token ' + this.apiToken; options.headers.accept = normaliseAcceptsHeader(options.headers.accept); var _callback = wrappedCallback(cb); var req; if (successStream) { req = request.get(options); req.on('error', function(err) { _callback(err); }); req.on('response', function(response) { if (response.statusCode === 200) { response.pipe(successStream); successStream.on('close', function() { _callback(null, response, {}); }); } else { stringFromStream(response, function(body) { _callback(null, response, body); }); } }); } else { req = request.get(options, _callback); } return req; }, // put(resource, [{query: query, successStream: writeStream},] callback) put: function (resource) { if (arguments.length < 2 || arguments.length > 3) { throw new BoxViewError(ErrorTypes.INVALID_USE, 'Incorrect number of arguments passed to ' + __filename + '.get(): ' + arguments.length); } var cb = arguments[arguments.length - 1]; var options = (arguments.length === 3) ? arguments[1] : {}; validateCommonArgs(resource, options, cb); options.qs = normaliseQuery(options.query); options.headers = options.headers || {}; options.uri = API.uriString(resource); options.headers.Authorization = 'Token ' + this.apiToken; options.headers.accept = normaliseAcceptsHeader(options.headers.accept); return request.put(options, wrappedCallback(cb)); }, // post(apiToken, resource, [{query: query, encodingOptions: encodingOptions},] callback) post: function(resource) { if (arguments.length < 2 || arguments.length > 3) { throw new BoxViewError(ErrorTypes.INVALID_USE, 'Incorrect number of arguments passed to '+__filename+'.post(): '+arguments.length); } var cb = arguments[arguments.length - 1]; var options = (arguments.length === 3) ? arguments[1] : {}; validateCommonArgs(resource, options, cb); options.qs = normaliseQuery(options.query); options.headers = options.headers || {}; options.uri = API.uriString(resource); options.headers.Authorization = 'Token '+this.apiToken; options.headers.accept = normaliseAcceptsHeader(options.headers.accept); return request.post(options, wrappedCallback(cb)); }, del: function (resource) { if (arguments.length < 2 || arguments.length > 3) { throw new BoxViewError(ErrorTypes.INVALID_USE, 'Incorrect number of arguments passed to ' + __filename + '.get(): ' + arguments.length); } var cb = arguments[arguments.length - 1]; var options = (arguments.length === 3) ? arguments[1] : {}; validateCommonArgs(resource, options, cb); options.qs = normaliseQuery(options.query); options.headers = options.headers || {}; options.uri = API.uriString(resource); options.headers.Authorization = 'Token ' + this.apiToken; options.headers.accept = normaliseAcceptsHeader(options.headers.accept); return request.del(options, wrappedCallback(cb)); } };