Comparing version 0.0.1 to 0.0.2
@@ -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
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
728244
71
16702
4