facebook-search
Version:
A simple Node.js search module for Facebook Graph API
208 lines (170 loc) • 5.17 kB
JavaScript
var https = require('https'),
url = require('url'),
qs = require('querystring'),
_ = require('underscore'),
fbutil = require('./facebook-utils');
function FacebookSearch(apiKey, apiSecret, opts) {
this.auth = {
key: apiKey,
secret: apiSecret,
token: undefined,
code: undefined
};
this.config = _.defaults((opts || {}), {
'redirect_uri': 'http://127.0.0.1:3000/',
'protocol': 'https',
'host': 'graph.facebook.com',
'oauth_path': '/oauth/access_token',
'search_path': '/search',
'scope': 'client_credentials'
});
this.paging = {};
};
FacebookSearch.prototype.getAuthorizationUrl = function(opts) {
opts = _.defaults((opts || {}), {
'redirect_uri': this.config.redirect_uri,
'protocol': this.config.protocol,
'scope': this.config.scope,
'host': 'www.facebook.com',
'path': '/dialog/oauth'
});
query = {
'client_id': this.auth.key,
'redirect_uri': opts.redirect_uri,
'scope': _.isArray(opts.scope) ? opts.scope.join(',') : opts.scope
};
return url.format({
protocol: opts.protocol,
host: opts.host,
pathname: opts.path,
query: query
});
};
FacebookSearch.prototype.handleAuthorizationResponse = function(res, cb) {
var data = qs.parse(res);
if(data.error) {
cb({error: {type: data.error_reason, message: data.error_description}});
} else {
this.auth.code = data.code;
this.requestAccessToken(cb);
}
};
FacebookSearch.prototype.handleSignedRequest = function(req) {
var data = fbutil.parseSignedRequest(req, this.auth.secret);
if(data) {
this.auth.token = data.oauth_token;
}
return this.auth.token;
};
FacebookSearch.prototype.setAccessToken = function(token) {
this.auth.token = token;
};
FacebookSearch.prototype.requestAccessToken = function(uri, cb) {
if(typeof uri === 'function') {
cb = uri;
uri = this.config.redirect_uri;
}
var self = this;
query = {
'client_id': self.auth.key,
'client_secret': self.auth.secret,
'redirect_uri': uri
};
if(self.auth.code) {
_.extend(query, {
'code': self.auth.code
});
} else {
_.extend(query, {
'grant_type': 'client_credentials'
});
}
_doRequest(url.format({
protocol: self.config.protocol,
host: self.config.host,
pathname: self.config.oauth_path,
query: query
}), function(err, tok) {
if(tok !== undefined) {
self.auth.token = qs.parse(tok).access_token;
}
cb(err, self.auth.token);
});
};
FacebookSearch.prototype.search = function(req, cb) {
if(arguments.length == 2) {
if(typeof req === 'string') {
req = {q: req};
}
var self = this;
req = _.defaults(req, {
'type': 'post'
});
this.request(self.config.search_path, req, function(err, res) {
if(res.error) {
cb(res.error);
} else {
self.paging = res.paging;
cb(null, res.data);
}
});
} else {
cb({error: {message: 'Not enough arguments'}});
}
};
FacebookSearch.prototype.request = function(path, opts, cb) {
if(typeof opts === 'function') {
cb = opts;
opts = {};
}
var self = this;
if(this.auth.token) {
opts = _.defaults(opts, {
'access_token': this.auth.token
});
_doRequest(url.format({protocol: self.config.protocol, host: self.config.host, pathname: path, query: opts}), function(err, res) {
cb(err, JSON.parse(res));
});
} else {
self.requestAccessToken(function(err, res) {
if(res) self.request(path, opts, cb);
else cb(err);
});
}
};
FacebookSearch.prototype.previous = function(cb) {
this.page('previous', cb);
};
FacebookSearch.prototype.next = function(cb) {
this.page('next', cb);
};
FacebookSearch.prototype.page = function(dir, cb) {
var self = this;
if(self.paging[dir]) {
_doRequest(self.paging[dir], function(err, res) {
res = JSON.parse(res);
if(res.error) {
cb(res.error);
} else {
self.paging = res.paging;
cb(null, res.data);
}
});
} else {
cb({error: {message: 'Invalid page'}});
}
}
function _doRequest(req, cb) {
if(typeof req === 'string') req = url.parse(req);
https.get(req, function(res) {
var buffer = '';
res.on('data', function(d) {
buffer += d;
}).on('end', function() {
cb(null, buffer);
});
}).on('error', function(e) {
cb(e);
});
};
module.exports = FacebookSearch;