Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

box-sdk

Package Overview
Dependencies
Maintainers
1
Versions
16
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

box-sdk - npm Package Compare versions

Comparing version 0.0.1 to 0.0.2

2

Gruntfile.js

@@ -14,3 +14,3 @@ 'use strict';

timeout: '60s',
slow: '10s',
slow: '20s',
require: [

@@ -17,0 +17,0 @@

@@ -23,2 +23,8 @@ 'use strict';

/**
* A Writable Stream.
* @external Writable
* @see {@link http://nodejs.org/api/stream.html#stream_class_stream_writable}
*/
var base = require('base-framework'),

@@ -45,2 +51,3 @@ _ = require('lodash'),

* @param {?string} [logLevel] - Optional log level. Defaults to {@linkcode info}.
* @param {?external:Writable} [logStream] - Optional writable stream for log output. Defaults to console.
* @see {@link https://www.npmjs.org/package/log#log-levels}

@@ -51,3 +58,3 @@ */

{
init: function (opts, logLevel) {
init: function (opts, logLevel, logStream) {
var self = this;

@@ -70,3 +77,3 @@ self.connections = {};

self.host = opts.host || 'localhost';
self.log = new Log(logLevel || 'info');
self.log = new Log(logLevel || 'info', logStream);
self.client_id = opts.client_id;

@@ -117,7 +124,7 @@ self.client_secret = opts.client_secret;

var tokenParams = {
client_id: self.client_id,
client_secret: self.client_secret,
grant_type: 'authorization_code',
code: params.code
},
client_id: self.client_id,
client_secret: self.client_secret,
grant_type: 'authorization_code',
code: params.code
},
authUrl = 'https://www.box.com/api/oauth2/token';

@@ -124,0 +131,0 @@

'use strict';
var request = require('request'),
url = require('url'),
_ = require('lodash'),
querystring = require('querystring'),
base = require('base-framework'),
fs = require('fs'),
path = require('path'),
Monologue = require('monologue.js')(_),
FormData = require('form-data');
url = require('url'),
_ = require('lodash'),
querystring = require('querystring'),
base = require('base-framework'),
fs = require('fs'),
path = require('path'),
async = require('async'),
Monologue = require('monologue.js')(_),
FormData = require('form-data');

@@ -21,350 +22,375 @@ /**

var Connection = base.createChild().addInstanceMethods(
/** @lends Connection.prototype */
{
init: function (box, email) {
this.email = email;
this.csrf = Math.random().toString(36).slice(2);
_.each(['host', 'port', 'log', 'client_id', 'client_secret'], function (key) {
this[key] = box[key];
}, this);
return this;
},
/** @lends Connection.prototype */
{
init: function (box, email) {
this.email = email;
this.csrf = Math.random().toString(36).slice(2);
this.concurrency = 7;
/**
* The returned URL should be provided to the end user when running in standalone mode.
* @summary Get the authentication URL to manually navigate to.
* @returns {string} The authentication URL.
*/
getAuthURL: function () {
if (this.auth_url) {
return this.auth_url;
}
var self = this,
destination = {
protocol: 'https',
host: 'www.box.com',
pathname: '/api/oauth2/authorize',
search: querystring.stringify({
response_type: 'code',
client_id: self.client_id,
state: self.csrf,
redirect_uri: 'http://' + self.host + ':' + self.port + '/authorize?id=' + self.email
})
};
_.each(['host', 'port', 'log', 'client_id', 'client_secret'], function (key) {
this[key] = box[key];
}, this);
self.auth_url = url.format(destination);
return self.auth_url;
},
this.queue = async.queue(function (task, cb) {
var r = request(task.opts, function (err, res, body) {
task.handler(err, res, body);
cb();
});
if (task.form) {
r._form = task.form;
}
if (task['content-length']) {
r.setHeader('content-length', task['content-length']);
}
}, this.concurrency);
/**
* Authentication token object for a connection.
* @typedef {Object} AuthTokens
* @property {string} access_token - The Access Token.
* @property {string} refresh_token - The Refresh Token.
* @property {number} [expires_in] - Optional lifetime value of access token in seconds.
* @property {Array} [restricted_to] - Optional (possibly) list of IP from which to grant access.
* @property {string} [token_type] - Optional. Value should normally be 'bearer'.
*/
return this;
},
/**
* Do not call this method directly.
* @summary Set the authentication tokens for this connection.
* @private
* @param {AuthTokens} tokens - The authentication tokens.
* @fires Connection#"tokens.set"
*/
_setTokens: function (tokens) {
_.merge(this, tokens);
/**
* Fires when access tokens have been set on this connection. Could be triggered more than once,
* so listeners must deregister after receiving the first event.
* Preferably use the {@link Connection#ready} method.
* @event Connection#"tokens.set"
* @type {AuthTokens}
* @see {@link Connection#ready}
*/
this.emit('tokens.set');
},
/**
* The returned URL should be provided to the end user when running in standalone mode.
* @summary Get the authentication URL to manually navigate to.
* @returns {string} The authentication URL.
*/
getAuthURL: function () {
if (this.auth_url) {
return this.auth_url;
}
var self = this,
destination = {
protocol: 'https',
host: 'www.box.com',
pathname: '/api/oauth2/authorize',
search: querystring.stringify({
response_type: 'code',
client_id: self.client_id,
state: self.csrf,
redirect_uri: 'http://' + self.host + ':' + self.port + '/authorize?id=' + self.email
})
};
/**
* Do not call this function directly.
* @summary Set the authentication tokens for this connection.
* @private
*/
_revokeAccess: function () {
delete this.access_token;
},
self.auth_url = url.format(destination);
return self.auth_url;
},
/**
* Standard {@linkcode fields/limit/offset} options, used in several connection methods.
* @external OptsFLO
* @see {@link https://developers.box.com/docs/#folders-retrieve-a-folders-items}
*/
/**
* Authentication token object for a connection.
* @typedef {Object} AuthTokens
* @property {string} access_token - The Access Token.
* @property {string} refresh_token - The Refresh Token.
* @property {number} [expires_in] - Optional lifetime value of access token in seconds.
* @property {Array} [restricted_to] - Optional (possibly) list of IP from which to grant access.
* @property {string} [token_type] - Optional. Value should normally be 'bearer'.
*/
/**
* Headers to pass alongwith a request on a connection.
* @typedef {Object} RequestHeaders
* @property {string} [If-Match] - If-Match ETAG.
* @property {string} [If-None-Match] - If-None-Match ETAG.
* @see {@link https://developers.box.com/docs/#if-match}
*/
/**
* Do not call this method directly.
* @summary Set the authentication tokens for this connection.
* @private
* @param {AuthTokens} tokens - The authentication tokens.
* @fires Connection#"tokens.set"
*/
_setTokens: function (tokens) {
_.merge(this, tokens);
/**
* Fires when access tokens have been set on this connection. Could be triggered more than once,
* so listeners must deregister after receiving the first event.
* Preferably use the {@link Connection#ready} method.
* @event Connection#"tokens.set"
* @type {AuthTokens}
* @see {@link Connection#ready}
*/
this.emit('tokens.set');
},
/**
* Options to configure the behaviour of the request itself.
* @typedef {Object} RequestConfig
* @property {number} [timeout] - The response timeout in milliseconds. Defaults to {@linkcode 120000}.
* @property {number} [num_retries] - The number of retries to attempt before giving up. {@linkcode 0}
* implies no retries. A negative number implies unlimited retries.
* @property {number} [ebackoff] - Initial backoff interval in milliseconds. The request dispatcher will
* wait for this interval before attempting a retry. The interval is doubled on each successive retry.
* Defaults to {@linkcode 1000}.
*/
/**
* Do not call this function directly.
* @summary Set the authentication tokens for this connection.
* @private
*/
_revokeAccess: function () {
delete this.access_token;
},
/**
* A Readable Stream.
* @external Readable
* @see {@link http://nodejs.org/api/stream.html#stream_class_stream_readable}
*/
/**
* Standard {@linkcode fields/limit/offset} options, used in several connection methods.
* @external OptsFLO
* @see {@link https://developers.box.com/docs/#folders-retrieve-a-folders-items}
*/
/**
* A Writable Stream.
* @external Writable
* @see {@link http://nodejs.org/api/stream.html#stream_class_stream_writable}
*/
/**
* Headers to pass alongwith a request on a connection.
* @typedef {Object} RequestHeaders
* @property {string} [If-Match] - If-Match ETAG.
* @property {string} [If-None-Match] - If-None-Match ETAG.
* @see {@link https://developers.box.com/docs/#if-match}
*/
/**
* Called after a request is performed on a connection.
* @callback requestCallback
* @param {Error} [error] - Any error that occurred.
* @param {Object} body - The response body.
*/
/**
* Options to configure the behaviour of the request itself.
* @typedef {Object} RequestConfig
* @property {number} [timeout] - The response timeout in milliseconds. Defaults to {@linkcode 120000}.
* @property {number} [num_retries] - The number of retries to attempt before giving up. {@linkcode 0}
* implies no retries. A negative number implies unlimited retries.
* @property {number} [ebackoff] - Initial backoff interval in milliseconds. The request dispatcher will
* wait for this interval before attempting a retry. The interval is doubled on each successive retry.
* Defaults to {@linkcode 1000}.
*/
/**
* Do not call this method directly.
* Use one of the wrapper API methods instead.
* @summary Perform an HTTP request on this connection.
* @private
* @param {(string | Array.<string>)} segments - The path segments to append to the API base URL.
* @param {string} method - The HTTP verb to use for this request.
* @param {requestCallback} callback - The callback to invoke (with possible errors) when the request returns.
* @param {?Object} [query] - A map of query parameters.
* @param {?Object} [payload] - The request payload.
* @param {?external:Readable} [data] - Readable stream representing file data to be uploaded.
* @param {?RequestHeaders} [headers] - Additional headers.
* @param {external:Writable} [pipe] - Writable stream representing file data to be saved.
* @param {?RequestConfig} [config] - Configure the request behaviour.
*/
_request: function (segments, method, callback, query, payload, data, headers, pipe, config) {
var methods = ['POST', 'GET', 'DELETE', 'PUT', 'OPTIONS'];
if (!_.contains(methods, method)) {
throw new Error('Unsupported method: ' + method);
}
/**
* A Readable Stream.
* @external Readable
* @see {@link http://nodejs.org/api/stream.html#stream_class_stream_readable}
*/
var self = this,
url;
/**
* Called after a request is performed on a connection.
* @callback requestCallback
* @param {Error} [error] - Any error that occurred.
* @param {Object} body - The response body.
*/
config = _.merge({
timeout: 120000,
num_retries: 3,
ebackoff: 1000
}, config || {});
/**
* Do not call this method directly.
* Use one of the wrapper API methods instead.
* @summary Perform an HTTP request on this connection.
* @private
* @param {(string | Array.<string>)} segments - The path segments to append to the API base URL.
* @param {string} method - The HTTP verb to use for this request.
* @param {requestCallback} callback - The callback to invoke (with possible errors) when the request returns.
* @param {?Object} [query] - A map of query parameters.
* @param {?Object} [payload] - The request payload.
* @param {?external:Readable} [data] - Readable stream representing file data to be uploaded.
* @param {?RequestHeaders} [headers] - Additional headers.
* @param {external:Writable} [pipe] - Writable stream representing file data to be saved.
* @param {?RequestConfig} [config] - Configure the request behaviour.
*/
_request: function (segments, method, callback, query, payload, data, headers, pipe, config) {
//Self-invocation with the same params should work transparently for the caller.
var methods = ['POST', 'GET', 'DELETE', 'PUT', 'OPTIONS'];
if (!_.contains(methods, method)) {
throw new Error('Unsupported method: ' + method);
}
if (_.isString(segments)) {
url = segments;
} else if (data) {
url = 'https://upload.box.com/api/2.0/' + segments.join('/');
} else {
url = 'https://www.box.com/api/2.0/' + segments.join('/');
}
var self = this,
url;
var opts = {
url: url,
method: method,
headers: {
Authorization: 'Bearer ' + self.access_token
},
timeout: config.timeout
};
config = _.merge({
timeout: 120000,
num_retries: 3,
ebackoff: 1000
}, config || {});
if (data) {
opts.json = true;
} else {
_.merge(opts, {
json: (_.contains(['POST', 'PUT'], method)) ? payload : true,
qs: query
});
}
if (_.isString(segments)) {
url = segments;
} else if (data) {
url = 'https://upload.box.com/api/2.0/' + segments.join('/');
} else {
url = 'https://www.box.com/api/2.0/' + segments.join('/');
}
headers = headers || {};
_.merge(opts.headers, headers);
var opts = {
url: url,
method: method,
headers: {
Authorization: 'Bearer ' + self.access_token
},
timeout: config.timeout
};
if (data) {
var form = new FormData();
if (data) {
opts.json = true;
} else {
_.merge(opts, {
json: (_.contains(['POST', 'PUT'], method)) ? payload : true,
qs: query
});
}
form.append('filename', data);
_.forIn(payload, function (value, key) {
form.append(key, value);
});
headers = headers || {};
_.merge(opts.headers, headers);
form.getLength(function (err, length) {
if (err) {
return callback(err);
}
if (data) {
var form = new FormData();
var r = request(opts, _handler);
r._form = form;
r.setHeader('content-length', length);
});
} else {
if (pipe && method === 'GET') {
opts.followRedirect = false;
}
request(opts, _handler);
}
form.append('filename', data);
_.forIn(payload, function (value, key) {
form.append(key, value);
});
function _handler(err, res, body) {
if (err) {
if (config.num_retries === 0) {
return callback(err);
}
form.getLength(function (err, length) {
if (err) {
return callback(err);
}
var ebck = config.ebackoff;
// var r = request(opts, _handler);
// r._form = form;
// r.setHeader('content-length', length);
self.queue.push({
opts: opts,
handler: _handler,
form: form,
'content-length': length
});
});
} else {
if (pipe && method === 'GET') {
opts.followRedirect = false;
}
// request(opts, _handler);
self.queue.push({
opts: opts,
handler: _handler
});
}
config.num_retries--;
config.ebackoff *= 2;
self.log.debug('Waiting %d milliseconds before retrying...', ebck);
function _handler(err, res, body) {
if (err) {
if (config.num_retries === 0) {
return callback(err);
}
return _.delay(function () {
self._request(segments, method, callback, query, payload, data, headers, pipe, config);
}, ebck);
}
var ebck = config.ebackoff;
switch (res.statusCode) {
case 200:
if (pipe) {
pipe.end(new Buffer(body, 'binary'));
} else {
callback(err, body);
}
break;
config.num_retries--;
config.ebackoff *= 2;
self.log.debug('Waiting %d milliseconds before retrying...', ebck);
case 202:
case 429:
self.log.debug('Status: %d, Retry-After: %d', res.statusCode, res.headers['retry-after']);
_.delay(function () {
self._request(segments, method, callback, query, payload, data, headers, pipe, config);
}, res.headers['retry-after'] * 1000);
break;
return _.delay(function () {
self._request(segments, method, callback, query, payload, data, headers, pipe, config);
}, ebck);
}
case 301:
opts.url = res.headers.location;
request(opts, _handler);
break;
switch (res.statusCode) {
case 200:
if (pipe) {
pipe.end(new Buffer(body, 'binary'));
} else {
callback(err, body);
}
break;
case 302:
request.get(res.headers.location, callback).pipe(pipe);
break;
case 202:
case 429:
var wait = res.headers['retry-after'] * 1000 + _.random(0, 10000);
self.log.debug('Status: %d, Retry-After: %d', res.statusCode, res.headers['retry-after']);
self.log.debug('Waiting %d milliseconds before retrying...', wait);
_.delay(function () {
self._request(segments, method, callback, query, payload, data, headers, pipe, config);
}, wait);
break;
case 400:
case 403:
case 405:
case 409:
case 412:
callback(new Error(JSON.stringify(body)));
break;
case 301:
opts.url = res.headers.location;
// request(opts, _handler);
self.queue.push({
opts: opts,
handler: _handler
});
break;
case 401:
//First check if stale access tokens still exist, and only then refresh.
//Prevents multiple refreshes.
if (self.isAuthenticated()) {
self.log.debug('Access token expired. Refreshing...');
self._revokeAccess();
var tokenParams = {
client_id: self.client_id,
client_secret: self.client_secret,
grant_type: 'refresh_token',
refresh_token: self.refresh_token
},
authUrl = 'https://www.box.com/api/oauth2/token';
case 302:
request.get(res.headers.location, callback).pipe(pipe);
break;
request.post({
url: authUrl,
form: tokenParams,
json: true
}, function (err, res, body) {
if (err) {
return callback(err);
}
if (res.statusCode === 200) {
self.log.debug('New tokens received.');
self._setTokens(body);
self._request(segments, method, callback, query, payload, data, headers, pipe, config);
} else {
callback(new Error(JSON.stringify(body)));
}
});
} else {
self.ready(function () {
self._request(segments, method, callback, query, payload, data, headers, pipe, config);
});
}
break;
case 400:
case 403:
case 404:
case 405:
case 409:
case 412:
callback(new Error(JSON.stringify(body)));
break;
case 404:
//Possibly the resource has just been created. Try again.
if (!_.isNumber(config.retry_404)) {
config.retry_404 = 1;
}
if (config.retry_404 === 0) {
return callback(new Error(JSON.stringify(body)));
}
case 401:
//First check if stale access tokens still exist, and only then refresh.
//Prevents multiple refreshes.
if (self.isAuthenticated()) {
self.log.debug('Access token expired. Refreshing...');
self._revokeAccess();
var tokenParams = {
client_id: self.client_id,
client_secret: self.client_secret,
grant_type: 'refresh_token',
refresh_token: self.refresh_token
},
authUrl = 'https://www.box.com/api/oauth2/token';
_.delay(function () {
self.log.debug('Retrying 404...');
request.post({
url: authUrl,
form: tokenParams,
json: true
}, function (err, res, body) {
if (err) {
return callback(err);
}
if (res.statusCode === 200) {
self.log.debug('New tokens received.');
self._setTokens(body);
self._request(segments, method, callback, query, payload, data, headers, pipe, config);
} else {
callback(new Error(JSON.stringify(body)));
}
});
} else {
self.ready(function () {
self._request(segments, method, callback, query, payload, data, headers, pipe, config);
});
}
break;
config.retry_404--;
self._request(segments, method, callback, query, payload, data, headers, pipe, config);
}, 3000);
break;
default:
callback(err, body);
}
}
},
default:
callback(err, body);
}
}
},
/**
* Wait for a connection to get ready.
* @param {optionalErrorCallback} callback - The callback with an optional err argument,
* to be invoked once ready.
*/
ready: function (callback) {
if (this.isAuthenticated()) {
callback();
} else {
this.once('tokens.*', callback);
}
},
/**
* Wait for a connection to get ready.
* @param {optionalErrorCallback} callback - The callback with an optional err argument,
* to be invoked once ready.
*/
ready: function (callback) {
if (this.isAuthenticated()) {
callback();
} else {
this.once('tokens.*', callback);
}
},
/**
* Determine id the connection has been authenticated.
* @returns {boolean} True if authenticated.
*/
isAuthenticated: function () {
return !_.isEmpty(this.access_token);
},
/**
* Determine id the connection has been authenticated.
* @returns {boolean} True if authenticated.
*/
isAuthenticated: function () {
return !_.isEmpty(this.access_token);
}
});
/**
* A connection starts with a default concurrency of {@linkcode 7}. Note that long-polling will consume a
* request from the request pool, so it is generally not a good idea to set this to less than {@linkcode 2}.
* @summary Set the maximum concurrent requests that this connection will process.
* @param {number} concurrency - The number of concurrent requests.
* @throws {RangeError} Concurrency must be an integer > {@linkcode 0}.
*/
setConcurrency: function (concurrency) {
if (concurrency < 1) {
throw new RangeError('Concurrency must be an integer > 0');
}
this.queue.concurrency = Math.floor(concurrency);
}
});
//Load API Methods
(function loadAPI(mainpath) {
var items = fs.readdirSync(mainpath);
_.each(items, function (item) {
var subpath = path.join(mainpath, item);
var stats = fs.statSync(subpath);
if (stats.isDirectory()) {
loadAPI(subpath);
} else if (stats.isFile() && item.match(/.*\.js$/)) {
require(subpath)(Connection);
}
});
var items = fs.readdirSync(mainpath);
_.each(items, function (item) {
var subpath = path.join(mainpath, item);
var stats = fs.statSync(subpath);
if (stats.isDirectory()) {
loadAPI(subpath);
} else if (stats.isFile() && item.match(/.*\.js$/)) {
require(subpath)(Connection);
}
});
})(path.join(__dirname, 'api'));

@@ -371,0 +397,0 @@

{
"name": "box-sdk",
"description": "Node.js client for Box Content API",
"version": "0.0.1",
"version": "0.0.2",
"homepage": "https://github.com/adityamukho/node-box-sdk",

@@ -6,0 +6,0 @@ "author": {

@@ -5,2 +5,3 @@ 'use strict';

var casper = require('casper').create();
casper.options.waitTimeout = 20000;

@@ -15,3 +16,5 @@ casper.start(casper.cli.args[0], function () {

casper.then(function () {
this.fillSelectors('form[name="consent_form"]', {}, true);
this.waitForSelector('form[name="consent_form"]', function () {
this.fillSelectors('form[name="consent_form"]', {}, true);
});
});

@@ -18,0 +21,0 @@

@@ -32,2 +32,3 @@ 'use strict';

exports.prepTestFolder = function (connection, done) {
var self = this;
var args = [connection.getAuthURL(), process.env.ICT_EMAIL_ID, process.env.ICT_PASSWORD];

@@ -38,19 +39,7 @@

assert.ifError(err);
connection.getFolderItems(0, null, function (err, result) {
connection.createFolder('test_nbsdk-' + self.uuid(), 0, function (err, result) {
if (err) {
return done(err);
}
var test_nbsdk = _.find(result.entries, {
name: 'test_nbsdk'
});
if (_.isEmpty(test_nbsdk)) {
connection.createFolder('test_nbsdk', 0, function (err, result) {
if (err) {
return done(err);
}
done(null, result.id);
});
} else {
done(null, test_nbsdk.id);
}
done(null, result.id);
});

@@ -57,0 +46,0 @@ });

@@ -36,3 +36,2 @@ 'use strict';

assert(result.entries instanceof Array);
assert.notEqual(result.entries.length, 0);

@@ -39,0 +38,0 @@ done();

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc