diff --git a/lib/graph.js b/lib/graph.js index d323029..066fe15 100644 --- a/lib/graph.js +++ b/lib/graph.js @@ -29,42 +29,46 @@ function extend(target) { * @private */ -var accessToken = null - , appSecret = null - , graphUrl = 'https://graph.facebook.com' - , graphVersion = '2.0' // default to the oldest version - , oauthDialogUrl = "http://www.facebook.com/v2.0/dialog/oauth?" // oldest version for auth - , oauthDialogUrlMobile = "http://m.facebook.com/v2.0/dialog/oauth?" // oldest version for auth - , requestOptions = {}; +//var accessToken = null +// , appSecret = null +// , graphUrl = 'https://graph.facebook.com' +// , graphVersion = '2.0' // default to the oldest version +// , oauthDialogUrl = "http://www.facebook.com/v2.0/dialog/oauth?" // oldest version for auth +// , oauthDialogUrlMobile = "http://m.facebook.com/v2.0/dialog/oauth?" // oldest version for auth +// , requestOptions = {}; /** * Library version */ -exports.version = '0.3.0'; +Client.prototype.version = '0.3.0'; /** * Graph Stream * * @param {String} method * @param {String} url - * @param {object/function} - postData + * @param {object/function} postData * - object to be used for post * - assumed to be a callback function if callback is undefined - * @param {function/undefined} - callback function + * @param {function/undefined} callback function + * @param {Client} client fbgraph client */ -function Graph(method, url, postData, callback) { - if (typeof callback === 'undefined') { - callback = postData; - postData = {}; +function Graph(method, url, postData, callback, client) { + if (typeof client === 'undefined') { + client = callback; + callback = postData; + postData = {}; } + this.client = client; + url = this.prepareUrl(url); this.callback = callback || noop; this.postData = postData; - this.options = extend({}, requestOptions); + this.options = extend({}, this.client.requestOptions); this.options.encoding = this.options.encoding || 'utf-8'; // these particular set of options should be immutable @@ -87,7 +91,7 @@ Graph.prototype.prepareUrl = function(url) { url = this.cleanUrl(url); if (url.substr(0,4) !== 'http') { - url = graphUrl + '/v' + graphVersion + url; + url = this.client.graphUrl + '/v' + this.client.graphVersion + url; } return url; @@ -107,20 +111,20 @@ Graph.prototype.cleanUrl = function(url) { // prep access token in url for appsecret proofing var regex = /access_token=([^&]*)/; var results = regex.exec(url); - var sessionAccessToken = results ? results[1] : accessToken; + var sessionAccessToken = results ? results[1] : this.client.accessToken; // add leading slash if (url.charAt(0) !== '/' && url.substr(0,4) !== 'http') url = '/' + url; // add access token to url - if (accessToken && url.indexOf('access_token=') === -1) { + if (this.client.accessToken && url.indexOf('access_token=') === -1) { url += ~url.indexOf('?') ? '&' : '?'; - url += "access_token=" + accessToken; + url += "access_token=" + this.client.accessToken; } // add appsecret_proof to the url - if (sessionAccessToken && appSecret && url.indexOf('appsecret_proof') === -1) { - var hmac = crypto.createHmac('sha256', appSecret); + if (sessionAccessToken && this.client.appSecret && url.indexOf('appsecret_proof') === -1) { + var hmac = crypto.createHmac('sha256', this.client.appSecret); hmac.update(sessionAccessToken); url += ~url.indexOf('?') ? '&' : '?'; @@ -226,6 +230,24 @@ Graph.prototype.post = function() { }; +function Client(){ +} +Client.prototype.constructor = Client; + +var defaultClient = Object.create(Client.prototype); +defaultClient.accessToken = null; +defaultClient.appSecret = null; +defaultClient.graphUrl = 'https://graph.facebook.com'; +defaultClient.graphVersion = '2.0'; // default to the oldest version +defaultClient.oauthDialogUrl = "http://www.facebook.com/v2.0/dialog/oauth?"; // oldest version for auth +defaultClient.oauthDialogUrlMobile = "http://m.facebook.com/v2.0/dialog/oauth?"; // oldest version for auth +defaultClient.requestOptions = {}; + +/** + * @type {Client} + */ +module.exports = defaultClient; + /** * Accepts an url an returns facebook * json data to the callback provided @@ -261,7 +283,7 @@ Graph.prototype.post = function() { * @param {function} callback */ -exports.get = function(url, params, callback) { +Client.prototype.get = function(url, params, callback) { if (typeof params === 'function') { callback = params; params = null; @@ -276,7 +298,7 @@ exports.get = function(url, params, callback) { url += qs.stringify(params); } - return new Graph('GET', url, callback); + return new Graph('GET', url, callback, this); }; /** @@ -292,17 +314,17 @@ exports.get = function(url, params, callback) { * @param {function} callback */ -exports.post = function (url, postData, callback) { +Client.prototype.post = function (url, postData, callback) { if (typeof url !== 'string') { return callback({ message: 'Graph api url must be a string' }, null); } if (typeof postData === 'function') { callback = postData; - postData = url.indexOf('access_token') !== -1 ? {} : {access_token: accessToken}; + postData = url.indexOf('access_token') !== -1 ? {} : {access_token: this.accessToken}; } - return new Graph('POST', url, postData, callback); + return new Graph('POST', url, postData, callback, this); }; /** @@ -315,7 +337,7 @@ exports.post = function (url, postData, callback) { * @param {function} callback */ -exports.del = function (url, postData, callback) { +Client.prototype.del = function (url, postData, callback) { if (!url.match(/[?|&]method=delete/i)) { url += ~url.indexOf('?') ? '&' : '?'; url += 'method=delete'; @@ -323,7 +345,7 @@ exports.del = function (url, postData, callback) { if (typeof postData === 'function') { callback = postData; - postData = url.indexOf('access_token') !== -1 ? {} : {access_token: accessToken}; + postData = url.indexOf('access_token') !== -1 ? {} : {access_token: this.accessToken}; } this.post(url, postData, callback); @@ -337,7 +359,7 @@ exports.del = function (url, postData, callback) { * @param {function} callback */ -exports.search = function (options, callback) { +Client.prototype.search = function (options, callback) { options = options || {}; var url = '/search?' + qs.stringify(options); return this.get(url, callback); @@ -345,7 +367,7 @@ exports.search = function (options, callback) { /** * Perform a batch query on the graph api - * + * * @param {Array} reqs An array containing queries * @param {[Object]} additionalData Additional data to send, e.g. attachments or the `include_headers` parameter. * @param {Function} callback @@ -353,7 +375,7 @@ exports.search = function (options, callback) { * @see https://developers.facebook.com/docs/graph-api/making-multiple-requests */ -exports.batch = function (reqs, additionalData, callback) { +Client.prototype.batch = function (reqs, additionalData, callback) { if (!(reqs instanceof Array)) { return callback({ message: 'Graph api batch requests must be an array' }, null); } @@ -364,9 +386,9 @@ exports.batch = function (reqs, additionalData, callback) { } return new Graph('POST', '', extend({}, { - access_token: accessToken, + access_token: this.accessToken, batch: JSON.stringify(reqs) - }, additionalData), callback); + }, additionalData), callback, this); }; @@ -384,7 +406,7 @@ exports.batch = function (reqs, additionalData, callback) { * @param {object} params * @param {function} callback */ -exports.fql = function (query, params, callback) { +Client.prototype.fql = function (query, params, callback) { if (typeof query !== 'string') query = JSON.stringify(query); var url = '/fql?q=' + encodeURIComponent(query); @@ -406,8 +428,8 @@ exports.fql = function (query, params, callback) { * @param {object} opts Options hash. { mobile: true } will return mobile oAuth URL * @returns the oAuthDialogUrl based on params */ -exports.getOauthUrl = function (params, opts) { - var url = (opts && opts.mobile) ? oauthDialogUrlMobile : oauthDialogUrl; +Client.prototype.getOauthUrl = function (params, opts) { + var url = (opts && opts.mobile) ? this.oauthDialogUrlMobile : this.oauthDialogUrl; return url + qs.stringify(params); }; @@ -423,7 +445,7 @@ exports.getOauthUrl = function (params, opts) { * @param {function} callback */ -exports.authorize = function (params, callback) { +Client.prototype.authorize = function (params, callback) { var self = this; this.get("/oauth/access_token", params, function(err, res) { @@ -443,7 +465,7 @@ exports.authorize = function (params, callback) { * @param {function} callback */ -exports.extendAccessToken = function (params, callback) { +Client.prototype.extendAccessToken = function (params, callback) { var self = this; params.grant_type = 'fb_exchange_token'; @@ -465,8 +487,8 @@ exports.extendAccessToken = function (params, callback) { * @param {Object} options */ -exports.setOptions = function (options) { - if (typeof options === 'object') requestOptions = options; +Client.prototype.setOptions = function (options) { + if (typeof options === 'object') this.requestOptions = options; return this; }; @@ -475,8 +497,8 @@ exports.setOptions = function (options) { * @returns the request options object */ -exports.getOptions = function() { - return requestOptions; +Client.prototype.getOptions = function() { + return this.requestOptions; }; /** @@ -484,8 +506,8 @@ exports.getOptions = function() { * @param {string} token */ -exports.setAccessToken = function(token) { - accessToken = token; +Client.prototype.setAccessToken = function(token) { + this.accessToken = token; return this; }; @@ -493,23 +515,23 @@ exports.setAccessToken = function(token) { * @returns the access token */ -exports.getAccessToken = function () { - return accessToken; +Client.prototype.getAccessToken = function () { + return this.accessToken; }; /** - * Set's the Graph API version. - * Note that you don't need to specify the 'v', just + * Set's the Graph API version. + * Note that you don't need to specify the 'v', just * add '2.1', '1.1' etc * @param {string} version */ -exports.setVersion = function (version) { +Client.prototype.setVersion = function (version) { // set version - graphVersion = version; + this.graphVersion = version; // update auth urls - oauthDialogUrl = "http://www.facebook.com/v"+version+"/dialog/oauth?"; // oldest version for auth - oauthDialogUrlMobile = "http://m.facebook.com/v"+version+"/dialog/oauth?"; // oldest version for auth + this.oauthDialogUrl = "http://www.facebook.com/v"+version+"/dialog/oauth?"; // oldest version for auth + this.oauthDialogUrlMobile = "http://m.facebook.com/v"+version+"/dialog/oauth?"; // oldest version for auth return this; }; @@ -520,8 +542,8 @@ exports.setVersion = function (version) { * @param {string} token */ -exports.setAppSecret = function(token) { - appSecret = token; +Client.prototype.setAppSecret = function(token) { + this.appSecret = token; return this; }; @@ -529,16 +551,16 @@ exports.setAppSecret = function(token) { * @returns the app secret */ -exports.getAppSecret = function () { - return appSecret; +Client.prototype.getAppSecret = function () { + return this.appSecret; }; /** * sets graph url */ -exports.setGraphUrl = function (url) { - graphUrl = url; +Client.prototype.setGraphUrl = function (url) { + this.graphUrl = url; return this; }; @@ -546,6 +568,15 @@ exports.setGraphUrl = function (url) { * @returns the graphUrl */ -exports.getGraphUrl = function() { - return graphUrl; +Client.prototype.getGraphUrl = function() { + return this.graphUrl; }; + +/** + * @returns {Client} a client inheriting from this one + */ + +Client.prototype.createClient = function() { + return Object.create(this); +}; +