Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
151 changes: 91 additions & 60 deletions lib/graph.js
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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;
Expand All @@ -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('?') ? '&' : '?';
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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;
Expand All @@ -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);
};

/**
Expand All @@ -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);
};

/**
Expand All @@ -315,15 +337,15 @@ 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';
}

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);
Expand All @@ -337,23 +359,23 @@ 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);
};

/**
* 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
*
* @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);
}
Expand All @@ -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);
};


Expand All @@ -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);
Expand All @@ -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);
};

Expand All @@ -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) {
Expand All @@ -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';
Expand All @@ -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;
};
Expand All @@ -475,41 +497,41 @@ exports.setOptions = function (options) {
* @returns the request options object
*/

exports.getOptions = function() {
return requestOptions;
Client.prototype.getOptions = function() {
return this.requestOptions;
};

/**
* Sets the access token
* @param {string} token
*/

exports.setAccessToken = function(token) {
accessToken = token;
Client.prototype.setAccessToken = function(token) {
this.accessToken = token;
return this;
};

/**
* @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;
};
Expand All @@ -520,32 +542,41 @@ exports.setVersion = function (version) {
* @param {string} token
*/

exports.setAppSecret = function(token) {
appSecret = token;
Client.prototype.setAppSecret = function(token) {
this.appSecret = token;
return this;
};

/**
* @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;
};

/**
* @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);
};