Skip to content
Browse files

Merge remote-tracking branch 'origin/master' into brainz

  • Loading branch information...
2 parents ab29aa4 + 1a075eb commit 495d395435409df08f51b7855f6d7f5fd52975f7 @othiym23 othiym23 committed Mar 21, 2012
View
5 Collections/Links/dataIn.js
@@ -9,6 +9,9 @@ var url = require('url');
var debug = false;
var dataStore, locker, logger;
+
+exports.process = false;
+
// internally we need these for happy fun stuff
exports.init = function(_locker, dStore, log) {
dataStore = dStore;
@@ -97,6 +100,7 @@ function processEncounter(e, cb)
}
var encounterQueue = async.queue(function(e, callback) {
+ if (!exports.process) return callback();
// immediately dequeue in case processing makes something go wrong
dataStore.dequeue(e);
// do all the dirty work to store a new encounter
@@ -123,6 +127,7 @@ var encounterQueue = async.queue(function(e, callback) {
}, 5);
exports.loadQueue = function() {
+ if (!exports.process) return;
dataStore.fetchQueue(function(err, docs) {
if(!docs) return;
for (var i = 0; i < docs.length; i++) {
View
1 Collections/Links/links.js
@@ -44,6 +44,7 @@ app.get('/update', function (req, res) {
// simple oembed util internal api
app.get('/embed', function (req, res) {
+ if (!dataIn.process) return res.send({});
oembed.fetch({url:req.query.url}, function (e) {
if(e) return res.send(e);
res.send({});
View
7 Connectors/Netflix/README.md
@@ -0,0 +1,7 @@
+If you request a Netflix API key from Mashery it may not work immediately. If you get 401 "invalid signature" errors try waiting an hour or two for it to be activated.
+
+TODO:
+=====
+
+- A ratings synclet
+- Verify that the Netflix API doesn't work with Authorization headers
View
63 Connectors/Netflix/auth.js
@@ -0,0 +1,63 @@
+module.exports = {
+ handler: function(host, apiKeys, done, req, res) {
+ var qs = require('querystring');
+ var request = require('request');
+ var url = require('url');
+ var OAlib = require('oauth').OAuth;
+
+ var callbackUrl = host + "auth/netflix/auth";
+
+ var OA = new OAlib('http://api.netflix.com/oauth/request_token',
+ 'http://api.netflix.com/oauth/access_token',
+ apiKeys.appKey,
+ apiKeys.appSecret,
+ '1.0',
+ callbackUrl,
+ 'HMAC-SHA1',
+ null,
+ { Accept: '*/*', Connection: 'close' });
+
+ var qs = url.parse(req.url, true).query;
+
+ // Second phase, after user authorization
+ if (qs && qs.oauth_token && req.session.token_secret) {
+ OA.getOAuthAccessToken(qs.oauth_token, req.session.token_secret, qs.oauth_verifier,
+ function(error, oauth_token, oauth_token_secret, results) {
+ if (error || !oauth_token) {
+ return done(new Error("oauth failed to get access token"));
+ }
+
+ // Note that we're also grabbing and storing
+ // the user ID from the queryString
+ done(null, {
+ consumerKey: apiKeys.appKey,
+ consumerSecret: apiKeys.appSecret,
+ token: oauth_token,
+ tokenSecret: oauth_token_secret,
+ userId: results.user_id,
+ applicationName: req.session.application_name
+ });
+ });
+
+ return;
+ }
+
+ // First phase, initiate user authorization
+ OA.getOAuthRequestToken({ oauth_callback: callbackUrl },
+ function(error, oauth_token, oauth_token_secret, results) {
+ if (error) {
+ return res.end("failed to get token: " + error);
+ }
+
+ // Stash the secret
+ req.session.token_secret = oauth_token_secret;
+
+ // Stash the application name in case Netflix needs it again
+ req.session.application_name = results.application_name.replace(/ /g, '+');
+
+ var loginUrl = 'https://api-user.netflix.com/oauth/login?application_name=' + req.session.application_name + '&oauth_callback=' + callbackUrl + '&oauth_consumer_key=' + apiKeys.appKey + '&oauth_token=' + oauth_token;
+
+ res.redirect(loginUrl);
+ });
+ }
+};
View
42 Connectors/Netflix/history.js
@@ -0,0 +1,42 @@
+var MAX_ITEMS = 250;
+
+exports.sync = require('./lib').genericSync('history', function(pi) {
+ return '/users/' + pi.auth.userId + '/rental_history/watched';
+}, function(pi) {
+ if (!pi.config) {
+ pi.config = {};
+ }
+
+ if (typeof pi.config.historyStart === 'undefined') {
+ pi.config.historyStart = 0;
+
+ return '?max_results=' + MAX_ITEMS;
+ }
+
+ return '?max_results=' + MAX_ITEMS + '&start_index=' + pi.config.historyStart;
+}, function(pi, js) {
+ var items = js.rental_history_item;
+
+ if (!js || !items || items.length === 0) {
+ pi.config.historyStart = 0;
+ pi.config.nextRun = 0;
+
+ return [];
+ }
+
+ if (js.number_of_results &&
+ pi.config.historyStart > parseInt(js.number_of_results, 10)) {
+ pi.config.historyStart = 0;
+ pi.config.nextRun = 0;
+ }
+
+ if (items.length < MAX_ITEMS) {
+ pi.config.historyStart = 0;
+ pi.config.nextRun = 0;
+ } else {
+ pi.config.historyStart += MAX_ITEMS;
+ pi.config.nextRun = -1;
+ }
+
+ return items;
+});
View
103 Connectors/Netflix/lib.js
@@ -0,0 +1,103 @@
+// node-oauth doesn't provide a method for created signed requests with
+// query string parameters instead of Authorization headers, so this is a hack.
+function authQueryStringFromUrl(OA, pi, host, url, path) {
+ var params = OA._prepareParameters(pi.auth.token, pi.auth.tokenSecret, 'HMAC-SHA1', url);
+
+ // Remove the OAuth signature from the parameters (we'll generate it again below)
+ for (var i in params) {
+ if (params[i][0] == 'oauth_signature') {
+ delete params[i];
+
+ break;
+ }
+ }
+
+ // OAuth query string parameters need to be sorted
+ // alphabetically before the signature is generated.
+ params = params.sort(function(a, b) {
+ return a[0] > b[0] ? 1 : a[0] < b[0] ? -1 : 0;
+ });
+
+ var output = [];
+
+ // Build the query string
+ for (var parameter in params) {
+ output.push(params[parameter][0] + '=' + params[parameter][1]);
+ }
+
+ // Add the signature of the query string to the end of the query string
+ output.push('oauth_signature=' + OA._encodeData(OA._getSignature('GET',
+ 'http://' + host + path, output.join('&'), pi.auth.tokenSecret)));
+
+ return '?' + output.join('&');
+}
+
+function parseXml(xml, callback) {
+ var xml2js = require('xml2js');
+
+ var parser = new xml2js.Parser();
+
+ parser.addListener('end', function(result) {
+ callback(result);
+ });
+
+ parser.parseString(xml);
+}
+
+exports.genericSync = function(type, pather, querier, arrayer) {
+ // pi: process info
+ return function(pi, cb) {
+ var OAlib = require('oauth').OAuth;
+ var OA = new OAlib(null, null, pi.auth.consumerKey, pi.auth.consumerSecret, '1.0', null, 'HMAC-SHA1', null);
+ var http = require('http');
+
+ var host = 'api.netflix.com';
+
+ var url = 'http://' + host + pather(pi) + querier(pi);
+
+ var queryString = authQueryStringFromUrl(OA, pi, host, url, pather(pi));
+
+ // Setup our own HTTP request since we're using
+ // query string authorization
+ var options = {
+ host: host,
+ port: 80,
+ path: pather(pi) + queryString,
+ headers: {
+ 'Accept': '*/*',
+ 'Connection': 'close'
+ }
+ };
+
+ // Send the request
+ http.get(options, function(res) {
+ res.setEncoding('utf8');
+
+ var data = '';
+
+ res.on('data', function(chunk) {
+ data += chunk;
+ });
+
+ // Once we've got the full response...
+ res.on('end', function() {
+ var xml;
+
+ // Try parsing it...
+ parseXml(data, function(xml) {
+ var array = {};
+
+ array[type] = arrayer(pi, xml);
+
+ // And return it using the passed in callback,
+ // with (optionally) updated auth and config data
+ cb(null, {
+ auth: pi.auth,
+ config: pi.config,
+ data: array
+ });
+ });
+ });
+ });
+ };
+};
View
21 Connectors/Netflix/package.json
@@ -0,0 +1,21 @@
+{
+ "author": "Beau Gunderson <beau@beaugunderson.com>",
+ "name": "netflix",
+ "description": "Netflix",
+ "version": "0.2.3",
+ "repository": {
+ "title": "Netflix",
+ "handle": "netflix",
+ "author": "Beau Gunderson <beau@beaugunderson.com>",
+ "update": "auto",
+ "github": "https://github.com/LockerProject/Locker",
+ "type": "connector",
+ "static": "false",
+ "url": ""
+ },
+ "engines": {
+ "node": ">=0.4.9"
+ },
+ "dependencies": {},
+ "devDependencies": {}
+}
View
8 Connectors/Netflix/synclets.json
@@ -0,0 +1,8 @@
+{
+ "mongoId": {
+ "history": "id"
+ },
+ "synclets": [
+ { "name": "history", "frequency": 86400, "threshold": 0 }
+ ]
+}
View
12 Connectors/Netflix/test.js
@@ -0,0 +1,12 @@
+var fs = require('fs');
+var auth = JSON.parse(fs.readFileSync("../../Me/netflix/me.json")).auth;
+
+console.error('auth', auth);
+
+var sync = require(process.argv[2]);
+
+sync.sync({ auth: auth }, function(e, js) {
+ console.error('error', e);
+
+ console.error("got js", JSON.stringify(js));
+});
View
57 Connectors/Withings/auth.js
@@ -0,0 +1,57 @@
+module.exports = {
+ handler: function(host, apiKeys, done, req, res) {
+ var qs = require('querystring');
+ var request = require('request');
+ var url = require('url');
+ var OAlib = require('oauth').OAuth;
+
+ var callbackUrl = host + "auth/withings/auth";
+
+ var OA = new OAlib('https://oauth.withings.com/account/request_token',
+ 'https://oauth.withings.com/account/access_token',
+ apiKeys.appKey,
+ apiKeys.appSecret,
+ '1.0',
+ callbackUrl,
+ 'HMAC-SHA1',
+ null,
+ { Accept: '*/*', Connection: 'close' });
+
+ var qs = url.parse(req.url, true).query;
+
+ // Second phase, after user authorization
+ if (qs && qs.oauth_token && req.session.token_secret) {
+ OA.getOAuthAccessToken(qs.oauth_token, req.session.token_secret, qs.oauth_verifier,
+ function(error, oauth_token, oauth_token_secret) {
+ if (error || !oauth_token) {
+ return done(new Error("oauth failed to get access token"));
+ }
+
+ // Note that we're also grabbing and storing
+ // the user ID from the queryString
+ done(null, {
+ consumerKey: apiKeys.appKey,
+ consumerSecret: apiKeys.appSecret,
+ token: oauth_token,
+ tokenSecret: oauth_token_secret,
+ userId: qs.userid
+ });
+ });
+
+ return;
+ }
+
+ // First phase, initiate user authorization
+ OA.getOAuthRequestToken({ oauth_callback: callbackUrl },
+ function(error, oauth_token, oauth_token_secret, oauth_authorize_url) {
+ if (error) {
+ return res.end("failed to get token: " + error);
+ }
+
+ // Stash the secret
+ req.session.token_secret = oauth_token_secret;
+
+ res.redirect('https://oauth.withings.com/account/authorize?oauth_token=' + oauth_token + '&oauth_callback=' + callbackUrl);
+ });
+ }
+};
View
95 Connectors/Withings/lib.js
@@ -0,0 +1,95 @@
+// node-oauth doesn't provide a method for created signed requests with
+// query string parameters instead of Authorization headers, so this is a hack.
+function authQueryStringFromUrl(OA, pi, host, url, path) {
+ var params = OA._prepareParameters(pi.auth.token, pi.auth.tokenSecret, 'HMAC-SHA1', url);
+
+ // Remove the OAuth signature from the parameters (we'll generate it again below)
+ for (var i in params) {
+ if (params[i][0] == 'oauth_signature') {
+ delete params[i];
+
+ break;
+ }
+ }
+
+ // OAuth query string parameters need to be sorted
+ // alphabetically before the signature is generated.
+ params = params.sort(function(a, b) {
+ return a[0] > b[0] ? 1 : a[0] < b[0] ? -1 : 0;
+ });
+
+ var output = [];
+
+ // Build the query string
+ for (var parameter in params) {
+ output.push(params[parameter][0] + '=' + params[parameter][1]);
+ }
+
+ // Add the signature of the query string to the end of the query string
+ output.push('oauth_signature=' + OA._encodeData(OA._getSignature('GET',
+ 'http://' + host + path, output.join('&'), pi.auth.tokenSecret)));
+
+ return '?' + output.join('&');
+}
+
+exports.deviceSync = function(device, pather, querier, arrayer) {
+ // pi: process info
+ return function(pi, cb) {
+ var OAlib = require('oauth').OAuth;
+ var OA = new OAlib(null, null, pi.auth.consumerKey, pi.auth.consumerSecret, '1.0', null, 'HMAC-SHA1', null);
+ var http = require('http');
+
+ var host = 'wbsapi.withings.net';
+
+ var url = 'http://' + host + pather(pi) + querier(pi);
+
+ var queryString = authQueryStringFromUrl(OA, pi, host, url, pather(pi));
+
+ // Setup our own HTTP request since we're using
+ // query string authorization
+ var options = {
+ host: host,
+ port: 80,
+ path: pather(pi) + queryString,
+ headers: {
+ 'Accept': '*/*',
+ 'Connection': 'close'
+ }
+ };
+
+ // Send the request
+ http.get(options, function(res) {
+ res.setEncoding('utf8');
+
+ var data = '';
+
+ res.on('data', function(chunk) {
+ data += chunk;
+ });
+
+ // Once we've got the full response...
+ res.on('end', function() {
+ var js;
+
+ // Try parsing it...
+ try {
+ js = JSON.parse(data);
+ } catch(E) {
+ return cb(E);
+ }
+
+ var array = {};
+
+ array[device] = arrayer(pi, js);
+
+ // And return it using the passed in callback,
+ // with (optionally) updated auth and config data
+ cb(null, {
+ auth: pi.auth,
+ config: pi.config,
+ data: array
+ });
+ });
+ });
+ };
+};
View
21 Connectors/Withings/package.json
@@ -0,0 +1,21 @@
+{
+ "author": "Beau Gunderson <beau@beaugunderson.com>",
+ "name": "withings",
+ "description": "Withings",
+ "version": "0.2.0",
+ "repository": {
+ "title": "Withings",
+ "handle": "withings",
+ "author": "Beau Gunderson <beau@beaugunderson.com>",
+ "update": "auto",
+ "github": "https://github.com/LockerProject/Locker",
+ "type": "connector",
+ "static": "false",
+ "url": ""
+ },
+ "engines": {
+ "node": ">=0.4.9"
+ },
+ "dependencies": {},
+ "devDependencies": {}
+}
View
42 Connectors/Withings/pressure.js
@@ -0,0 +1,42 @@
+var MAX_ITEMS = 50;
+
+exports.sync = require('./lib').deviceSync('pressure', function(pi) {
+ return '/measure';
+}, function(pi) {
+ if (!pi.config) {
+ pi.config = {};
+ }
+
+ if (typeof pi.config.pressureStart === 'undefined') {
+ pi.config.pressureStart = 0;
+ }
+
+ if (pi.config.pressureStart === 0) {
+ return '?action=getmeas&devtype=4&limit=' + MAX_ITEMS + '&userid=' + pi.auth.userId;
+ }
+
+ return '?action=getmeas&devtype=4&limit=' + MAX_ITEMS + '&offset=' + pi.config.pressureStart + '&userid=' + pi.auth.userId;
+}, function(pi, js) {
+ var items;
+
+ if (js.body && js.body.measuregrps) {
+ items = js.body.measuregrps;
+ }
+
+ if (!js || !items || items.length === 0) {
+ pi.config.pressureStart = 0;
+ pi.config.nextRun = 0;
+
+ return [];
+ }
+
+ if (items.length < MAX_ITEMS) {
+ pi.config.pressureStart = 0;
+ pi.config.nextRun = 0;
+ } else {
+ pi.config.pressureStart += MAX_ITEMS;
+ pi.config.nextRun = -1;
+ }
+
+ return items;
+});
View
42 Connectors/Withings/scale.js
@@ -0,0 +1,42 @@
+var MAX_ITEMS = 50;
+
+exports.sync = require('./lib').deviceSync('scale', function(pi) {
+ return '/measure';
+}, function(pi) {
+ if (!pi.config) {
+ pi.config = {};
+ }
+
+ if (typeof pi.config.scaleStart === 'undefined') {
+ pi.config.scaleStart = 0;
+ }
+
+ if (pi.config.scaleStart === 0) {
+ return '?action=getmeas&devtype=1&limit=' + MAX_ITEMS + '&userid=' + pi.auth.userId;
+ }
+
+ return '?action=getmeas&devtype=1&limit=' + MAX_ITEMS + '&offset=' + pi.config.scaleStart + '&userid=' + pi.auth.userId;
+}, function(pi, js) {
+ var items;
+
+ if (js.body && js.body.measuregrps) {
+ items = js.body.measuregrps;
+ }
+
+ if (!js || !items || items.length === 0) {
+ pi.config.scaleStart = 0;
+ pi.config.nextRun = 0;
+
+ return [];
+ }
+
+ if (items.length < MAX_ITEMS) {
+ pi.config.scaleStart = 0;
+ pi.config.nextRun = 0;
+ } else {
+ pi.config.scaleStart += MAX_ITEMS;
+ pi.config.nextRun = -1;
+ }
+
+ return items;
+});
View
10 Connectors/Withings/synclets.json
@@ -0,0 +1,10 @@
+{
+ "mongoId": {
+ "scale": "grpid",
+ "pressure": "grpid"
+ },
+ "synclets": [
+ { "name": "scale", "frequency": 43200, "threshold": 0 },
+ { "name": "pressure", "frequency": 43200, "threshold": 0 }
+ ]
+}
View
12 Connectors/Withings/test.js
@@ -0,0 +1,12 @@
+var fs = require('fs');
+var auth = JSON.parse(fs.readFileSync("../../Me/withings/me.json")).auth;
+
+console.error('auth', auth);
+
+var sync = require(process.argv[2]);
+
+sync.sync({ auth: auth }, function(e, js){
+ console.error('error', e);
+
+ console.error("got js", JSON.stringify(js));
+});
View
22 Connectors/Zeo/auth.js
@@ -0,0 +1,22 @@
+module.exports = {
+ handler: function (host, apiKeys, done, req, res) {
+ if (req.method === 'POST') {
+ done(null, {
+ appKey: apiKeys.appKey,
+ username: req.body.username,
+ password: req.body.password
+ });
+
+ return;
+ }
+
+ res.writeHead(200, { 'Content-Type': 'text/html' });
+
+ res.end('Please enter your Zeo credentials:' +
+ '<form method="post">' +
+ '<p><label>Username: <input name="username" type="textbox" size="32" /></label></p>' +
+ '<p><label>Password: <input name="password" type="password" size="32" /></label></p>' +
+ '<input type="submit" value="Save!">' +
+ '</form>');
+ }
+};
View
62 Connectors/Zeo/lib.js
@@ -0,0 +1,62 @@
+exports.callApi = function(pi, cb, pather, querier, arrayer) {
+ var https = require('https');
+
+ var authorization = 'Basic ' + new Buffer(pi.auth.username + ':' +
+ pi.auth.password).toString('base64');
+
+ var options = {
+ host: 'api.myzeo.com',
+ port: 8443,
+ path: '/zeows/api/v1/json' + pather(pi) + querier(pi),
+ headers: {
+ 'Accept': '*/*',
+ 'Connection': 'close',
+ 'Referer': pi.uri,
+ 'Authorization': authorization
+ }
+ };
+
+ // Send the request
+ https.get(options, function(res) {
+ res.setEncoding('utf8');
+
+ var data = '';
+
+ res.on('data', function(chunk) {
+ data += chunk;
+ });
+
+ // Once we've got the full response...
+ res.on('end', function() {
+ cb(data);
+ });
+ });
+};
+
+exports.genericSync = function(type, pather, querier, arrayer) {
+ return function(pi, cb) {
+ exports.callApi(pi, function(data) {
+ var js;
+
+ try {
+ js = JSON.parse(data);
+ } catch(E) {
+ return cb(err);
+ }
+
+ arrayer(pi, js, function(arrayData) {
+ var array = {};
+
+ array[type] = arrayData;
+
+ // And return it using the passed in callback,
+ // with (optionally) updated auth and config data
+ cb(null, {
+ auth: pi.auth,
+ config: pi.config,
+ data: array
+ });
+ });
+ }, pather, querier, arrayer);
+ };
+};
View
21 Connectors/Zeo/package.json
@@ -0,0 +1,21 @@
+{
+ "author": "Beau Gunderson <beau@beaugunderson.com>",
+ "name": "zeo",
+ "description": "Zeo",
+ "version": "0.1.2",
+ "repository": {
+ "title": "Zeo",
+ "handle": "zeo",
+ "author": "Beau Gunderson <beau@beaugunderson.com>",
+ "update": "auto",
+ "github": "https://github.com/LockerProject/Locker",
+ "type": "connector",
+ "static": "false",
+ "url": ""
+ },
+ "engines": {
+ "node": ">=0.4.9"
+ },
+ "dependencies": {},
+ "devDependencies": {}
+}
View
47 Connectors/Zeo/sleep.js
@@ -0,0 +1,47 @@
+var async = require('async'),
+ lib = require('./lib');
+
+exports.sync = lib.genericSync('sleep', function(pi) {
+ return '/sleeperService/getAllDatesWithSleepData';
+}, function(pi) {
+ return '?key=' + pi.auth.appKey;
+}, function(pi, js, cb) {
+ var dates = js.response.dateList.date;
+
+ if (!js || !dates) {
+ return [];
+ }
+
+ // For each date with a sleep record...
+ async.mapSeries(dates, function(date, mapped) {
+ // Retrieve the record...
+ lib.callApi(pi, function(data) {
+ var js;
+
+ try {
+ js = JSON.parse(data);
+ } catch(E) {
+ mapped(E);
+ }
+
+ var record = js.response.sleepRecord;
+
+ if (!js || !record) {
+ mapped();
+ }
+
+ record.id = record.startDate.year + '-' +
+ record.startDate.month + '-' +
+ record.startDate.day;
+
+ mapped(null, record);
+ }, function(pi) {
+ return '/sleeperService/getSleepRecordForDate';
+ }, function(pi) {
+ return '?key=' + pi.auth.appKey + '&date=' + date.year + '-' + date.month + '-' + date.day;
+ });
+ }, function(err, results){
+ // And return them all up the chain.
+ cb(results);
+ });
+});
View
5 Connectors/Zeo/synclets.json
@@ -0,0 +1,5 @@
+{
+ "synclets": [
+ { "name": "sleep", "frequency": 86400, "threshold": 0 }
+ ]
+}
View
10 Connectors/Zeo/test.js
@@ -0,0 +1,10 @@
+var fs = require('fs');
+var pi = JSON.parse(fs.readFileSync("../../Me/zeo/me.json"));
+
+var sync = require(process.argv[2]);
+
+sync.sync(pi, function(e, js) {
+ console.error('error', e);
+
+ console.error("got js", JSON.stringify(js));
+});
View
1 Ops/registry.js
@@ -91,6 +91,7 @@ exports.app = function (app) {
app.get('/auth/:id', authIsAwesome);
app.get('/auth/:id/auth', authIsAuth);
+ app.post('/auth/:id/auth', express.bodyParser(), authIsAuth);
app.get('/deauth/:id', deauthIsAwesomer);
};

0 comments on commit 495d395

Please sign in to comment.
Something went wrong with that request. Please try again.