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

Commit

Permalink
Merge pull request #13 from jamestalmage/use-firebase-copy
Browse files Browse the repository at this point in the history
drop mockfirebase for offline firebase-copy
  • Loading branch information
urish committed Oct 23, 2015
2 parents 9843628 + 43f0f06 commit 5b63e34
Show file tree
Hide file tree
Showing 8 changed files with 255 additions and 143 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
/.idea
/node_modules
/coverage
2 changes: 0 additions & 2 deletions .npmignore

This file was deleted.

8 changes: 5 additions & 3 deletions .travis.yml
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
language: node_js
node_js:
- "stable"
- "0.10"
- "0.12"
- "4.0.0"
before_script:
- npm install -g grunt-cli

after_script:
- cat ./coverage/lcov.info | ./node_modules/coveralls/bin/coveralls.js

sudo: false
45 changes: 0 additions & 45 deletions Gruntfile.js

This file was deleted.

167 changes: 112 additions & 55 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,11 @@

var _ = require('lodash');
var WebSocketServer = require('ws').Server;
var mockfirebase = require('mockfirebase');
var Ruleset = require('targaryen/lib/ruleset');
var RuleDataSnapshot = require('targaryen/lib/rule-data-snapshot');
var firebaseHash = require('./lib/firebaseHash');
var Promise = require('native-or-bluebird'); // jshint ignore:line
var firebaseCopy = require('firebase-copy');

var loggingEnabled = false;

Expand All @@ -21,10 +22,33 @@ function _log(message) {
}
}

function getSnap(ref) {
return new Promise(function (resolve) {
ref.once('value', function (snap) {
resolve(snap);
});
});
}

function getValue(ref) {
return getSnap(ref).then(function (snap) {
return snap.val();
});
}

function getExport(ref) {
return getSnap(ref).then(function (snap) {
return snap.exportVal();
});
}

function FirebaseServer(port, name, data) {
this.Firebase = firebaseCopy();
this.name = name || 'mock.firebase.server';
this.mockFb = new mockfirebase.MockFirebase('https://' + this.name + '/', data);
this.mockFb.autoFlush(1);
this.Firebase.goOffline();
this.baseRef = new this.Firebase('ws://fakeserver.firebaseio.com');

this.baseRef.set(data || null);

this._wss = new WebSocketServer({
port: port
Expand Down Expand Up @@ -57,78 +81,103 @@ FirebaseServer.prototype = {
send({d: {r: requestId, b: {s: 'permission_denied', d: 'Permission denied'}}, t: 'd'});
}

function handleListen(requestId, path, fbRef) {
_log('Client listen ' + path);
function ruleSnapshot(fbRef) {
return getExport(fbRef.root()).then(function (exportVal) {
return new RuleDataSnapshot(RuleDataSnapshot.convert(exportVal));
});
}

function tryRead(requestId, path, fbRef) {
if (server._ruleset) {
var dataSnap = new RuleDataSnapshot(RuleDataSnapshot.convert(fbRef.root().getData()));
var result = server._ruleset.tryRead(path, dataSnap);
if (!result.allowed) {
_log('Permission denied for client to read from ' + path + ': ' + result.info);
permissionDenied(requestId);
}
return;
return ruleSnapshot(fbRef).then(function (dataSnap) {
var result = server._ruleset.tryRead(path, dataSnap);
if (!result.allowed) {
permissionDenied(requestId);
throw new Error('Permission denied for client to read from ' + path + ': ' + result.info);
}
return true;
});
}
return Promise.resolve(true);
}

var currentData = fbRef.getData();
if ((typeof currentData !== 'undefined') && (currentData !== null)) {
pushData(path, fbRef.getData());
function tryWrite(requestId, path, fbRef, newData) {
if (server._ruleset) {
return ruleSnapshot(fbRef).then(function (dataSnap) {
var result = server._ruleset.tryWrite(path, dataSnap, newData);
if (!result.allowed) {
permissionDenied(requestId);
throw new Error('Permission denied for client to write to ' + path + ': ' + result.info);
}
return true;
});
}
send({d: {r: requestId, b: {s: 'ok', d: {}}}, t: 'd'});
fbRef.on('value', function (snap) {
if (snap.val()) {
pushData(path, fbRef.getData());
}
});
return Promise.resolve(true);
}

function handleListen(requestId, path, fbRef) {
_log('Client listen ' + path);

tryRead(requestId, path, fbRef)
.then(function () {
var sendOk = true;
fbRef.on('value', function (snap) {
if (snap.val()) {
pushData(path, snap.exportVal());
}
if (sendOk) {
sendOk = false;
send({d: {r: requestId, b: {s: 'ok', d: {}}}, t: 'd'});
}
});
})
.catch(_log);
}

function handleUpdate(requestId, path, fbRef, newData) {
_log('Client update ' + path);

var checkPermission = Promise.resolve(true);

if (server._ruleset) {
var dataSnap = new RuleDataSnapshot(RuleDataSnapshot.convert(fbRef.root().getData()));
var mergedData = _.assign(fbRef.getData(), newData);
var result = server._ruleset.tryWrite(path, dataSnap, mergedData);
if (!result.allowed) {
_log('Permission denied for client to write to ' + path + ': ' + result.info);
permissionDenied(requestId);
}
return;
checkPermission = getExport(fbRef).then(function (currentData) {
var mergedData = _.assign(currentData, newData);
return tryWrite(requestId, path, fbRef, mergedData);
});
}

fbRef.update(newData, function () {
// TODO check for failure
checkPermission.then(function () {
fbRef.update(newData);
send({d: {r: requestId, b: {s: 'ok', d: {}}}, t: 'd'});
});
}).catch(_log);
}

function handleSet(requestId, path, fbRef, newData, hash) {
_log('Client set ' + path);

if (server._ruleset) {
var dataSnap = new RuleDataSnapshot(RuleDataSnapshot.convert(fbRef.root().getData()));
var result = server._ruleset.tryWrite(path, dataSnap, newData);
if (!result.allowed) {
_log('Permission denied for client to write to ' + path + ': ' + result.info);
permissionDenied(requestId);
}
return;
}
var progress =
tryWrite(requestId, path, fbRef, newData);

if (typeof hash !== 'undefined') {
var calculatedHash = firebaseHash(fbRef.getData());
if (hash !== calculatedHash) {
pushData(path, fbRef.getData());
send({d: {r: requestId, b: {s: 'datastale', d: 'Transaction hash does not match'}}, t: 'd'});
return;
}
progress = progress.then(function () {
return getSnap(fbRef);
}).then (function (snap) {
var calculatedHash = firebaseHash(snap.exportVal());
if (hash !== calculatedHash) {
pushData(path, snap.exportVal());
send({d: {r: requestId, b: {s: 'datastale', d: 'Transaction hash does not match'}}, t: 'd'});
throw new Error('Transaction hash does not match: ' + hash + ' !==' + calculatedHash);
}
});
}

fbRef.set(newData, function () {
// TODO check for failure
pushData(path, fbRef.getData());
send({d: {r: requestId, b: {s: 'ok', d: {}}}, t: 'd'});
});
progress.then(function () {
fbRef.set(newData);
fbRef.once('value', function (snap) {
pushData(path, snap.exportVal());
send({d: {r: requestId, b: {s: 'ok', d: {}}}, t: 'd'});
});
}).catch(_log);
}

ws.on('message', function (data) {
Expand All @@ -143,7 +192,7 @@ FirebaseServer.prototype = {
path = parsed.d.b.p.substr(1);
}
var requestId = parsed.d.r;
var fbRef = path ? this.mockFb.child(path) : this.mockFb;
var fbRef = path ? this.baseRef.child(path) : this.baseRef;
if (parsed.d.a === 'l' || parsed.d.a === 'q') {
handleListen(requestId, path, fbRef);
}
Expand All @@ -163,8 +212,16 @@ FirebaseServer.prototype = {
this._ruleset = new Ruleset(rules);
},

getData: function () {
return this.mockFb.getData();
getSnap: function (ref) {
return getSnap(ref || this.baseRef);
},

getValue: function (ref) {
return getValue(ref || this.baseRef);
},

getExport: function (ref) {
return getExport(ref || this.baseRef);
},

close: function () {
Expand Down
37 changes: 20 additions & 17 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,11 @@
"description": "Simple Firebase Server",
"main": "index.js",
"scripts": {
"test": "grunt test"
"test": "npm run lint && npm run cover && npm run test-es5",
"cover": "istanbul cover ./node_modules/.bin/_mocha -- $(node-version --lt-4.0.0 --compilers js:babel/register) test/*.spec.js",
"test-es5": "mocha test/es5-only-test.js",
"debug": "mocha $(node-version --lt-4.0.0 --compilers js:babel/register)",
"lint": "jshint index.js lib/*.js test/*.js"
},
"repository": {
"type": "git",
Expand All @@ -20,29 +24,28 @@
"url": "https://github.com/urish/firebase-server/issues"
},
"homepage": "https://github.com/urish/firebase-server",
"config": {
"blanket": {
"pattern": [
"lib",
"index.js"
],
"data-cover-never": "node_modules"
}
},
"files": [
"index.js",
"lib/*.js",
"CHANGELOG.md"
],
"devDependencies": {
"babel": "^5.8.23",
"bluebird": "^1.2.0",
"co": "^4.6.0",
"coveralls": "^2.11.4",
"faye-websocket": "0.10.0",
"firebase": "^2.2.5",
"grunt": "^0.4.5",
"grunt-contrib-jshint": "^0.11.2",
"grunt-mocha-cov": "^0.4.0",
"load-grunt-tasks": "^3.2.0",
"lodash": "^3.9.3",
"istanbul": "^0.4.0",
"jshint": "^2.8.0",
"mocha": "^2.2.5",
"mockery": "^1.4.0"
"mockery": "^1.4.0",
"node-version-cli": "^1.1.4"
},
"dependencies": {
"firebase-copy": "^0.1.1",
"lodash": "^3.10.1",
"mockfirebase": "^0.11.0",
"native-or-bluebird": "^1.2.0",
"targaryen": "^1.1.4",
"ws": "0.7.2"
}
Expand Down
Loading

0 comments on commit 5b63e34

Please sign in to comment.