Last active
December 14, 2015 00:09
-
-
Save nikcorg/4997311 to your computer and use it in GitHub Desktop.
The produce of a HelsinkiJS mini-workshop. Pulls in the profile images of members of the HelsinkiJS group and renders them as a webpage.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Import the required modules | |
var fs = require("fs"); | |
var parseUrl = require("url").parse; | |
var http = require("http"); | |
var express = require("express"); | |
var handlebars = require("handlebars"); | |
var Q = require("q"); | |
var FB = require('fb'); | |
// Read the template file from disk | |
var index = fs.readFileSync("index.html", "utf-8"); | |
// Pre-compile the Handlerbars template | |
var template = handlebars.compile(index); | |
// Create an Expres.JS app | |
var app = express(); | |
// Application settings | |
var helsinkiJSGroupId = "208755712538736"; | |
var FBApp = { | |
ID: "<fb app id>", | |
secret: "<fb app secret>" | |
}; | |
/** | |
* Helper for doing HTTP requests in Node.js | |
* @param {string} method The HTTP request method | |
* @param {string} url The URL to fetch | |
* @return {promise} Returns a (Q.js) promise/deferred | |
*/ | |
function requestWithPromise(method, url) { | |
var def = Q.defer(); | |
var parsed = parseUrl(url); | |
var opts = { | |
method: method, | |
hostname: parsed.hostname, | |
port: parsed.port, | |
path: parsed.path, | |
headers: {} | |
}; | |
var data = ""; | |
var req = | |
http.request(opts). | |
on("response", function (res) { | |
res. | |
on("data", function (chunk) { | |
data += String(chunk); | |
}). | |
on("end", function () { | |
def.resolve([res.statusCode, res]); | |
}); | |
}). | |
on("error", function () { | |
def.reject(); | |
}). | |
end(); | |
return def.promise; | |
} | |
/** | |
* Request an access token from FB | |
* @return {promise} Returns a (Q.js) promise/deferred | |
*/ | |
function getAccessToken() { | |
var defer = Q.defer(); | |
FB.api('oauth/access_token', { | |
client_id: FBApp.ID, | |
client_secret: FBApp.secret, | |
grant_type: 'client_credentials' | |
}, function (res) { | |
if(!res || res.error) { | |
defer.reject(res); | |
return; | |
} | |
var accessToken = res.access_token; | |
FB.setAccessToken(accessToken); | |
defer.resolve(); | |
}); | |
return defer.promise; | |
} | |
/** | |
* Fetch the group member data from FB | |
* @return {promise} Returns a (Q.js) promise/deferred | |
*/ | |
function getGroupMembers() { | |
var defer = Q.defer(); | |
FB.api(helsinkiJSGroupId + "/members", function (fbres) { | |
if(!fbres || fbres.error) { | |
console.log(!fbres ? 'error occurred' : fbres.error); | |
defer.reject(fbres.error); | |
} | |
defer.resolve(fbres); | |
}); | |
return defer.promise; | |
} | |
/** | |
* Returns the user id's of an array of user objects | |
* @param {array} res Array of user object | |
* @return {array} Array of user id's | |
*/ | |
function extractUserIds(res) { | |
return res.data.map(function (user) { return user.id; }); | |
} | |
/** | |
* Fetch a URL to a single users profile image | |
* @param {string} userId The user's id | |
* @return {string} The URL to the profile image | |
*/ | |
function fetchUserProfileImage(userId) { | |
var defer = Q.defer(); | |
// Not using the FB.api, because it chokes on the redirect to the image as | |
// it attempts to parse it as JSON. Also, we're really only interested in | |
// the location header in the response. | |
requestWithPromise("GET", "http://graph.facebook.com/" + userId + "/picture"). | |
spread(function (status, res) { | |
defer.resolve(res.headers.location); | |
}, function () { | |
defer.reject("graph request failed"); | |
}); | |
return defer.promise; | |
} | |
/** | |
* Fetch URL's to a user's profile image | |
* @param {String} ids Array of user id's | |
* @return {array} Array of URL's | |
*/ | |
function fetchProfileImages(ids) { | |
var defer = Q.defer(); | |
var promises = ids.map(function (id) { | |
return fetchUserProfileImage(id); | |
}); | |
Q.all(promises). | |
then(function (images) { | |
defer.resolve(images); | |
}, function (err) { | |
console.log("single profile fetch error", err); | |
}). | |
done(); | |
return defer.promise; | |
} | |
/** | |
* Returns a dumb input echoing function, with a prefix message | |
* @param {string} msg Message to prepend | |
* @return {function} | |
*/ | |
function makeErrH(msg) { | |
return function () { | |
console.log(msg, arguments); | |
}; | |
} | |
// Bind a handler for the / URL (on localhost) | |
app.get("/", function (req, res) { | |
getAccessToken(). | |
then(getGroupMembers, makeErrH("access token failed")). | |
then(extractUserIds, makeErrH("group data failed")). | |
then(fetchProfileImages, makeErrH("profile images failed")). | |
then(function (images) { | |
// Send the response | |
res.send( | |
// Render the template | |
template({ images: images }) | |
); | |
}, function () { | |
res.send("Sorry, there was an error :("); | |
}). | |
done(); | |
}); | |
// Bind the express.js app on port 8080 on localhost | |
app.listen(8080); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!doctype html> | |
<html> | |
<head> | |
<title>HelsinkiJS Group Members</title> | |
</head> | |
<body> | |
<h1>Members of the HelsinkiJS Facebook Group</h1> | |
{{#images}} | |
<img src="{{.}}"> | |
{{/images}} | |
</body> | |
</html> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
{ | |
"name": "HSJS Workshop", | |
"version": "0.0.0", | |
"description": "HelsinkiJS Pair Programming", | |
"dependencies": { | |
"q": "*", | |
"handlebars": "*", | |
"express": "*", | |
"facebook-node-sdk": "Thuzi/facebook-node-sdk" | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment