Skip to content
This repository has been archived by the owner on Mar 19, 2019. It is now read-only.

Commit

Permalink
Bug 971993 — Store user ID to push URL mappings.
Browse files Browse the repository at this point in the history
r=abr
  • Loading branch information
almet committed Mar 10, 2014
1 parent f4bbda1 commit 3beef09
Show file tree
Hide file tree
Showing 9 changed files with 187 additions and 41 deletions.
9 changes: 8 additions & 1 deletion config/development.json
Expand Up @@ -2,5 +2,12 @@
"host": "localhost",
"port": 5000,
"macSecret": "263ceaa5546dce837191be98db91e852ae8d050d6805a402272e0c776193cfba",
"encryptionSecret": "7c69b9ca88e4f127f7280368f7055646"
"encryptionSecret": "7c69b9ca88e4f127f7280368f7055646",
"urlsStore": {
"engine": "mongo",
"settings": {
"name": "urlsStore",
"connectionString": "mongodb://127.0.0.1:27017/loop"
}
}
}
9 changes: 8 additions & 1 deletion config/test.json
Expand Up @@ -4,5 +4,12 @@
"macSecret": "263ceaa5546dce837191be98db91e852ae8d050d6805a402272e0c776193cfba",
"encryptionSecret": "7c69b9ca88e4f127f7280368f7055646",
"invalidMacSecret": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
"invalidEncryptionSecret": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
"invalidEncryptionSecret": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
"urlsStore": {
"engine": "mongo",
"settings": {
"name": "urlsStore",
"connectionString": "mongodb://127.0.0.1:27017/loop_test"
}
}
}
16 changes: 16 additions & 0 deletions loop/config.js
@@ -1,5 +1,16 @@
"use strict";
var convict = require('convict');
var format = require('util').format;

function validateStoreConfig(val) {
if (!val)
throw new Error("Should be defined");

["engine", "settings"].forEach(function(key) {
if (!val.hasOwnProperty(key))
throw new Error(format("Should have a %s property", key));
});
}

function hexKeyOfSize(size) {
return function check(val) {
Expand Down Expand Up @@ -43,6 +54,11 @@ var conf = convict({
default: "",
env: "ENCRYPTING_SECRET"
},
urlsStore: {
doc: "The configuration for the urlsStore",
format: validateStoreConfig,
default: ""
}
});


Expand Down
22 changes: 20 additions & 2 deletions loop/index.js
Expand Up @@ -8,6 +8,7 @@ var express = require('express');
var tokenlib = require('./tokenlib');
var conf = require('./config.js');
var auth = require('./authentication');
var getStore = require('./stores').getStore;
var app = express();

app.use(express.json());
Expand All @@ -18,6 +19,11 @@ var tokenManager = new tokenlib.TokenManager({
encryptionSecret: conf.get('encryptionSecret')
});

var urlsStore = getStore(
conf.get('urlsStore'),
{unique: ["user", "simplepushURL"]}
);

function validateSimplePushURL(reqDataObj) {
if (typeof reqDataObj !== 'object')
throw new Error('missing request data');
Expand Down Expand Up @@ -49,9 +55,21 @@ app.post('/registration', auth.isAuthenticated, function(req, res) {
return res.json(400, {error: err.message});
}

return res.json(200, "ok");
urlsStore.add({
user: req.user,
simplepushURL: req.body.simple_push_url
}, function(err, record){
if (err) {
return res.json(503, err);
}

return res.json(200, "ok");
});
});

app.listen(conf.get('port'), conf.get('host'));

module.exports = app;
module.exports = {
app: app,
urlsStore: urlsStore
};
9 changes: 9 additions & 0 deletions loop/stores/index.js
@@ -0,0 +1,9 @@
"use strict";

function getStore(conf, options) {
options = options || {};
var Store = require('./' + conf.engine + '.js');
return new Store(conf.settings, options);
}

module.exports = {getStore: getStore};
3 changes: 2 additions & 1 deletion loop/stores/memory.js
Expand Up @@ -14,7 +14,8 @@
* @param {Object} options Options object
* @return {MemoryStore}
*/
module.exports = function MemoryStore(options) {
module.exports = function MemoryStore(settings, options) {
"use strict";
var _db = [],
_options = options || {unique: []};

Expand Down
53 changes: 28 additions & 25 deletions loop/stores/mongo.js
Expand Up @@ -11,57 +11,60 @@ var MongoClient = require('mongodb').MongoClient;
*
* - {Array} unique: list of fields which compound value should be unique.
*
* @param {String} connectionString MongoDB connection string
* @param {String} name Store name, used to name the collection
* @param {Object} options Options object
* @param {Object} settings Settings object
* @param {Object} options Options object
* @return {MongoStore}
*/
module.exports = function MongoStore(connectionString, name, options) {
module.exports = function MongoStore(settings, options) {
"use strict";

var _db,
_options = options || {unique: []};
_coll,
_options = options || {unique: []},
_settings = settings || {};

if (!connectionString) {
throw new Error("The connectionString argument is required");
if (!_settings.hasOwnProperty('connectionString')) {
throw new Error("The connectionString setting is required");
}

if (!name) {
throw new Error("The name argument is required");
if (!_settings.hasOwnProperty('name')) {
throw new Error("The name setting is required");
}


/**
* Ensures the database is connected, sends back an instance of the
* db. Creates defined unique index if any.
*
* @private
* @param {Function} cb Callback(err, db)
* @param {Function} cb Callback(err, collection)
*/
function _ensureConnected(cb) {
if (_db) {
cb(null, _db);
if (_coll) {
cb(null, _coll);
return;
}
MongoClient.connect(connectionString, function(err, resultDb) {
MongoClient.connect(_settings.connectionString, function(err, resultDb) {
if (err) {
cb(err);
return;
}
_db = resultDb;
_coll = _db.collection(_settings.name);
if (!Array.isArray(_options.unique) || _options.unique.length === 0) {
cb(null, _db);
cb(null, _coll);
return;
}
var defs = _options.unique.reduce(function(obj, field) {
obj[field] = 1;
return obj;
}, {});
_db.collection(name).ensureIndex(defs, {unique: true}, function(err) {
_coll.ensureIndex(defs, {unique: true}, function(err) {
if (err) {
cb(err);
return;
}
cb(null, _db);
cb(null, _coll);
});
});
}
Expand All @@ -73,7 +76,7 @@ module.exports = function MongoStore(connectionString, name, options) {
* @return {String}
*/
get name() {
return name;
return _settings.name;
},

/**
Expand All @@ -83,12 +86,12 @@ module.exports = function MongoStore(connectionString, name, options) {
* @param {Function} cb Callback(err, record)
*/
add: function(record, cb) {
_ensureConnected(function(err, db) {
_ensureConnected(function(err, coll) {
if (err) {
cb(err);
return;
}
db.collection(name).insert(record, function(err, records) {
coll.insert(record, function(err, records) {
if (err) {
cb(err);
return;
Expand All @@ -105,12 +108,12 @@ module.exports = function MongoStore(connectionString, name, options) {
* @param {Function} cb Callback(err, record)
*/
find: function(query, cb) {
_ensureConnected(function(err, db) {
_ensureConnected(function(err, coll) {
if (err) {
cb(err);
return;
}
db.collection(name).find(query).toArray(cb);
coll.find(query).toArray(cb);
});
},

Expand All @@ -121,12 +124,12 @@ module.exports = function MongoStore(connectionString, name, options) {
* @param {Function} cb Callback(err, record|null)
*/
findOne: function(query, cb) {
_ensureConnected(function(err, db) {
_ensureConnected(function(err, coll) {
if (err) {
cb(err);
return;
}
db.collection(name).findOne(query, cb);
coll.findOne(query, cb);
});
},

Expand All @@ -135,14 +138,14 @@ module.exports = function MongoStore(connectionString, name, options) {
* @param {Function} cb Callback(err)
*/
drop: function(cb) {
_ensureConnected(function(err, db) {
_ensureConnected(function(err, coll) {
if (err) {
cb(err);
return;
}
try {
// drop() is a synchronous operation
db.collection(name).drop();
coll.drop();
cb(null);
} catch (err) {
cb(err);
Expand Down
71 changes: 65 additions & 6 deletions test/functional_test.js
Expand Up @@ -7,9 +7,10 @@ var expect = require("chai").expect;
var request = require("supertest");
var sinon = require("sinon");

var app = require("../loop");
var app = require("../loop").app;
var urlsStore = require("../loop").urlsStore;
var tokenlib = require("../loop/tokenlib");
var conf = require('../loop/config.js');
var conf = require("../loop/config.js");
var auth = require("../loop/authentication");

var ONE_MINUTE = 60 * 60 * 1000;
Expand All @@ -23,16 +24,17 @@ function getMiddlewares(method, url) {

describe("HTTP API exposed by the server", function() {

var sandbox, expectedAssertion, pushURL;
var sandbox, expectedAssertion, pushURL, user;

beforeEach(function() {
sandbox = sinon.sandbox.create();
expectedAssertion = "BID-ASSERTION";
user = "alexis@notmyidea.org";

// Mock the calls to the external BrowserID verifier.
sandbox.stub(auth, "verify", function(assertion, audience, cb){
if (assertion === expectedAssertion)
cb(null, "alexis@notmyidea.org", {});
cb(null, user, {});
else
cb("error");
});
Expand All @@ -43,13 +45,17 @@ describe("HTTP API exposed by the server", function() {
'gXwSLAH2VS8qKyZ1eLNTQSX6_AEeH73ohUy2A==';
});

afterEach(function() {
afterEach(function(done) {
sandbox.restore();
urlsStore.drop(function() {
done();
});
});

describe("authentication middleware", function() {
var jsonReq;

// Create a route with the auth middleware installed.
app.post('/with-middleware', auth.isAuthenticated, function(req, res) {
res.json(200, req.user);
});
Expand Down Expand Up @@ -199,7 +205,60 @@ describe("HTTP API exposed by the server", function() {
.expect(200).end(done);
});

it("should store push url", function() {
it("should store push url", function(done) {
jsonReq
.send({'simple_push_url': pushURL})
.expect(200).end(function(err, res) {
if (err) {
throw err;
}

urlsStore.findOne({user: user}, function(err, record) {
if (err) {
throw err;
}

expect(record.simplepushURL).eql(pushURL);
done();
});
});
});

it("should be able to store multiple push urls for one user",
function(done) {
function addPushURL(url, callback) {
request(app)
.post('/registration')
.set('Authorization', 'BrowserID ' + expectedAssertion)
.type('json')
.send({'simple_push_url': pushURL})
.expect('Content-Type', /json/)
.expect(200).end(callback);
}

addPushURL("http://url1", function(err, res) {
if (err) {
throw err;
}
addPushURL("http://url2", function(err, res) {
urlsStore.find({user: user}, function(err, records) {
if (err) {
throw err;
}
expect(records.length).eql(2);
done();
});
});
});
});

it("should answer a 503 if the database isn't available", function(done) {
sandbox.stub(urlsStore, "add", function(record, cb) {
cb("error");
});
jsonReq
.send({'simple_push_url': pushURL})
.expect(503).end(done);
});
});

Expand Down

0 comments on commit 3beef09

Please sign in to comment.