diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..4df1318 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,22 @@ +# EditorConfig helps developers define and maintain consistent +# coding styles between different editors and IDEs +# editorconfig.org + +root = true + +# Change these settings to your own preference +[*] +indent_style = space +indent_size = 4 +tab_width = 4 + +# Matches the exact files either package.json or .travis.yml +[{package.json,.travis.yml}] +indent_style = space +indent_size = 2 + +# We recommend you to keep these unchanged +end_of_line = lf +charset = utf-8 +trim_trailing_whitespace = true +insert_final_newline = true diff --git a/.gitignore b/.gitignore index 45d62d8..be65907 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,28 @@ +.idea + +/dev + *.sw? + +# Logs +logs +*.log + +# Runtime data +pids +*.pid +*.seed + +# Directory for instrumented libs generated by jscoverage/JSCover +lib-cov + +# Compiled binary addons (http://nodejs.org/api/addons.html) +build/Release + +# Dependency directory +# Commenting this out is preferred by some people, see +# https://www.npmjs.org/doc/misc/npm-faq.html#should-i-check-my-node_modules-folder-into-git- +node_modules + +# Users Environment Variables +.lock-wscript diff --git a/.npmignore b/.npmignore new file mode 100644 index 0000000..978d2f4 --- /dev/null +++ b/.npmignore @@ -0,0 +1,13 @@ +.*.swp +._* +.DS_Store +.editorconfig +.git +.gitignore +.hg +.lock-wscript +.wafpickle-* +CVS +npm-debug.log +travis.yml +/docs diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 0000000..89deefa --- /dev/null +++ b/.travis.yml @@ -0,0 +1,6 @@ +language: node_js +node_js: + - "0.12" +before_script: + - "sh -e /etc/init.d/xvfb start" + - npm install node-red-firebase diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..94dc9f9 --- /dev/null +++ b/LICENSE @@ -0,0 +1,22 @@ +The MIT License (MIT) + +Copyright (c) 2014 André L. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + diff --git a/README.md b/README.md index 87d9945..037d185 100644 --- a/README.md +++ b/README.md @@ -1,27 +1,66 @@ # Firebase nodes for Node-RED -Check it out! Now you can access your Firebase data with Node-RED! +[![Dependency Status](https://gemnasium.com/vergissberlin/node-red-firebase.svg)](https://gemnasium.com/vergissberlin/node-red-firebase) [![Build Status](https://api.travis-ci.org/vergissberlin/node-red-firebase.png?branch=master)](https://travis-ci.org/vergissberlin/node-red-firebase) [![Inline docs](http://inch-ci.org/github/vergissberlin/node-red-firebase.svg?branch=master)](http://inch-ci.org/github/vergissberlin/node-red-firebase) [![Issues](http://img.shields.io/github/issues/vergissberlin/node-red-firebase.svg)](https://github.com/vergissberlin/node-red-firebase/issues "GitHub ticket system") [![npm version](https://img.shields.io/npm/v/node-red-contrib-keymetrics.png)](https://npmjs.org/package/node-red-firebase "View this project on npm") [![Gitter](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/vergissberlin/node-red-firebase?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge) [![Greenkeeper badge](https://badges.greenkeeper.io/vergissberlin/node-red-firebase.svg)](https://greenkeeper.io/) + +--- + +Check it out! Now you can access your Firebase data with Node-RED! This allows you to automate Firebase data manipulation or generate custom events based on what's going on with your data store. -Installing node-red-firebase ----------------------------- - npm install firebase - cd nodes/ - git clone https://github.com/hovissimo/node-red-firebase +![Inline docs](docs/node-watch-200.png) ![Inline docs](docs/node-query-200.png) ![Inline docs](docs/node-modify-200.png) + + +## Dedicated to + +**If you are using** + +- the fantastic program [Node-RED](http://nodered.org), +- and you wanna interact with [Firebase API](https://www.firebase.com) [1](#glossary), + +**that node is made for you!** + +--- + +## Features -Check out the demo flows ------------------------ -To see the Firebase nodes in action, you can start Node-RED with +1. **Modify node** + - [SET](https://www.firebase.com/docs/web/api/firebase/set.html), [PUSH](https://www.firebase.com/docs/web/api/firebase/push.html), [UPDATE](https://www.firebase.com/docs/web/api/firebase/update.html), [REMOVE](https://www.firebase.com/docs/web/api/firebase/remove.html) record +2. **Watch node** + - Watch [on changes](https://www.firebase.com/docs/web/api/query/on.html) _Firebase_ API childs and send content to the output of the node +3. **Query node** + - Query to a value of a Firebase node - node red nodes/node-red-firebase/demo_flows.json - +## Installation -Note: You'll need to register your own Firebase account, and edit all of the Firebase nodes to use your personal Firebase URL. + cd ~/.node-red + npm install node-red-firebase + node-red -v + +Open your *Node-RED* Frontend and you will find the new node under the group *output*. **Happy wiring!** + +## Demo +Check out the demo flows to see the Firebase nodes in action, you can start Node-RED with + + node-red node_modules/node-red-firebase/demo_flows.json It's easiest to see what's going on if you have the live Firebase view open in another browser window while you interact with the flows. -Have questions? Found a bug? ------------------------------ -Please submit issues to the Github issue tracker +## Bugs, questions, contribute +- **Found a bug?** Please submit issues to the [Github issue tracker](https://github.com/vergissberlin/node-red-firebase/issues). +- **Have questions?** Please use [Gitter](https://gitter.im/vergissberlin/node-red-firebase?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) to get in contact with me +- **Wanna contribute?** Please make a [fork](https://github.com/vergissberlin/node-red-firebase#fork-destination-box) an send me an pull request. + + +## Thanks +Special thanks to +- Dave Conway-Jones and Nick O'Leary from IBM, founders of Node-RED. +- [hvissimo](https://github.com/hovissimo) for base of the Firebase node. + + +--- + +**Glossary ** + +1. *[PM2](https://github.com/Unitech/pm2) Firebase is a cloud services provider and backend as a service company based in San Francisco, California. The company makes a number of products for software developers building mobile or web applications.* diff --git a/docs/node-modify-200.png b/docs/node-modify-200.png new file mode 100644 index 0000000..8eb6837 Binary files /dev/null and b/docs/node-modify-200.png differ diff --git a/docs/node-modify-300.png b/docs/node-modify-300.png new file mode 100644 index 0000000..541e35f Binary files /dev/null and b/docs/node-modify-300.png differ diff --git a/docs/node-modify-500.png b/docs/node-modify-500.png new file mode 100644 index 0000000..afe442d Binary files /dev/null and b/docs/node-modify-500.png differ diff --git a/docs/node-query-200.png b/docs/node-query-200.png new file mode 100644 index 0000000..c4fce29 Binary files /dev/null and b/docs/node-query-200.png differ diff --git a/docs/node-query-300.png b/docs/node-query-300.png new file mode 100644 index 0000000..56acadb Binary files /dev/null and b/docs/node-query-300.png differ diff --git a/docs/node-query-500.png b/docs/node-query-500.png new file mode 100644 index 0000000..aca2f5d Binary files /dev/null and b/docs/node-query-500.png differ diff --git a/docs/node-watch-200.png b/docs/node-watch-200.png new file mode 100644 index 0000000..984f4a2 Binary files /dev/null and b/docs/node-watch-200.png differ diff --git a/docs/node-watch-300.png b/docs/node-watch-300.png new file mode 100644 index 0000000..3094e0b Binary files /dev/null and b/docs/node-watch-300.png differ diff --git a/docs/node-watch-500.png b/docs/node-watch-500.png new file mode 100644 index 0000000..ca0b5a6 Binary files /dev/null and b/docs/node-watch-500.png differ diff --git a/docs/settings-modify-500.png b/docs/settings-modify-500.png new file mode 100644 index 0000000..e098782 Binary files /dev/null and b/docs/settings-modify-500.png differ diff --git a/firebase_login.html b/firebase_login.html new file mode 100644 index 0000000..a85459c --- /dev/null +++ b/firebase_login.html @@ -0,0 +1,113 @@ + + + diff --git a/firebase_login.js b/firebase_login.js new file mode 100644 index 0000000..e16404e --- /dev/null +++ b/firebase_login.js @@ -0,0 +1,144 @@ +module.exports = function (RED) { + 'use strict'; + + /** + * FirebaseLoginNode + * + * The Server Definition - this opens (and closes) the connection + * + * @param n + * @constructor + */ + function FirebaseLoginNode(n) { + + var Firebase, + node = this, + firebaseStatus = require('./utility/status'); + + RED.nodes.createNode(this, n); + + this.appid = n.appid; + this.type = n.type; + this.uid = n.uid; + this.email = n.email; + this.password = n.password; + this.secret = n.secret; + + Firebase = require('firebase'); + + // Retrieve the config node + this.server = RED.nodes.getNode(n.server); + + if (this.credentials + && this.credentials.appid + && this.credentials.type) { + + firebaseStatus.connecting(this); + + this.url = 'https://' + this.credentials.appid + '.firebaseio.com'; + global.refFirebase = new Firebase(this.url); + switch (this.credentials.type) { + case 'custom': + LoginTypeCustom(node, firebaseStatus); + break; + case 'email': + LoginTypeEmail(node); + break; + } + } + else { + this.error('Check your credentials! (Login node)'); + } + + } + + /** + * Fire base login via user and secret + * @constructor + * @return void + */ + function LoginTypeCustom(node) { + var FirebaseLoginCustom, + firebaseLoginCustom; + + FirebaseLoginCustom = require('firebase-login-custom'); + + node.log('Firebase login custom'); + + if (node.credentials.secret && + node.credentials.uid) { + node.data = { + uid: node.credentials.uid, + secret: node.credentials.secret + }; + + firebaseLoginCustom = new FirebaseLoginCustom(node.refFirebase, + { + uid: node.data.uid + }, + { + secret: node.data.secret + }, + function (error) { + if (error !== null) { + node.error('Login error with custom login'); + node.error(error); + } else { + node.log('Login successful'); + } + } + ); + } else { + node.error('Check your secret!'); + } + } + + /** + * Fire base login via password and email + * @constructor + * @return void + */ + function LoginTypeEmail(node) { + var FirebaseLoginEmail, + firebaseLoginEmail; + + FirebaseLoginEmail = require('firebase-login-email'); + + node.log('Firebase login email'); + + if (node.credentials.email && + node.credentials.password) { + + node.data = { + email: node.credentials.email, + password: node.credentials.password + }; + + firebaseLoginEmail = new FirebaseLoginEmail( + global.refFirebase, + node.data, + function (error) { + if (error !== null) { + node.error('Login error with custom login'); + node.error(error); + } else { + node.log('Login successful'); + } + } + ); + } else { + node.error('Check your email credentials!'); + } + } + + RED.nodes.registerType('firebase login', FirebaseLoginNode, { + credentials: { + appid: {type: 'text'}, + type: {type: 'text'}, + uid: {type: 'text'}, + email: {type: 'text'}, + password: {type: 'password'}, + secret: {type: 'text'} + } + }); +}; diff --git a/firebase_modify.html b/firebase_modify.html index de7ca04..e948cbe 100644 --- a/firebase_modify.html +++ b/firebase_modify.html @@ -1,7 +1,12 @@ diff --git a/firebase_modify.js b/firebase_modify.js index 843cf5e..7bf1894 100644 --- a/firebase_modify.js +++ b/firebase_modify.js @@ -1,40 +1,59 @@ -module.exports = function(RED) { - "use strict"; - var Firebase = require("firebase"); +module.exports = function (RED) { + 'use strict'; function FirebaseModify(n) { - RED.nodes.createNode(this,n); - this.firebaseurl = n.firebaseurl; + var Firebase = require('firebase'), + firebaseStatus = require('./utility/status'); + + RED.nodes.createNode(this, n); + this.child = n.child; + this.credentials = RED.nodes.getNode(n.firebaselogin).credentials; + this.firebasepath = n.firebasepath; this.method = n.method; - this.firebase = new Firebase(this.firebaseurl); - - switch (this.method) { - case "set": - case "update": - case "push": - // To prevent code repetition, call the Firebase API function based on method directly - this.on('input', function(msg) { - // get path from msg or default to / - var childpath = (this.child) ? msg[this.child] : ""; - // make sure the path starts with / - childpath = (childpath.indexOf("/") == 0) ? childpath : "/" + childpath; - - this.firebase.child(childpath)[this.method](msg.payload); - }); - break; - case "remove": - // Remove method expects first argument to be a function, so we call it differently - this.on('input', function(msg) { - // get path from msg or default to / - var childpath = (this.child) ? msg[this.child] : ""; - // make sure the path starts with / - childpath = (childpath.indexOf("/") == 0) ? childpath : "/" + childpath; - - this.firebase.child(childpath)[this.method](); - }); - break; + + // Status + firebaseStatus.connecting(this); + + // Retrieve the config node + if (!this.credentials.appid) { + firebaseStatus.error(this, 'Check credentials!'); + this.error('You need to setup Firebase credentials!'); + } else { + this.firebaseurl = 'https://' + this.credentials.appid + '.firebaseio.com/' + this.firebasepath; + this.firebase = new Firebase(this.firebaseurl); + + // Status + firebaseStatus.checkStatus(this); + + switch (this.method) { + case 'set': + case 'update': + case 'push': + // To prevent code repetition, call the Firebase API function based on method directly + this.on('input', function (msg) { + // get path from msg or default to / + var childpath = (this.child) ? msg[this.child] : ''; + // make sure the path starts with / + childpath = (childpath.indexOf('/') == 0) ? childpath : '/' + childpath; + + this.firebase.child(childpath)[this.method](msg.payload); + }); + break; + case 'remove': + // Remove method expects first argument to be a function, so we call it differently + this.on('input', function (msg) { + // get path from msg or default to / + var childpath = (this.child) ? msg[this.child] : ''; + // make sure the path starts with / + childpath = (childpath.indexOf('/') == 0) ? childpath : '/' + childpath; + + this.firebase.child(childpath)[this.method](); + }); + break; + } } } - RED.nodes.registerType("firebase modify", FirebaseModify); -} + + RED.nodes.registerType('firebase modify', FirebaseModify); +}; diff --git a/firebase_query.html b/firebase_query.html index c88bf0a..a3e2599 100644 --- a/firebase_query.html +++ b/firebase_query.html @@ -1,7 +1,12 @@ diff --git a/firebase_query.js b/firebase_query.js index 4b0c509..36ff40a 100644 --- a/firebase_query.js +++ b/firebase_query.js @@ -1,25 +1,43 @@ -module.exports = function(RED) { - "use strict"; - var Firebase = require("firebase"); +module.exports = function (RED) { + 'use strict'; function sendMessageFromSnapshot(msg, snapshot) { msg.href = snapshot.ref().toString(); msg.payload = snapshot.val(); this.send(msg); } + function FirebaseQuery(n) { - RED.nodes.createNode(this,n); + var Firebase = require('firebase'), + firebaseStatus = require('./utility/status'); + + RED.nodes.createNode(this, n); - this.firebaseurl = n.firebaseurl; this.child = n.child; - this.firebase = new Firebase(this.firebaseurl); + this.credentials = RED.nodes.getNode(n.firebaselogin).credentials; + this.firebasepath = n.firebasepath; + + // Status + firebaseStatus.connecting(this); - this.on('input', function(msg) { - var childpath = (this.child) ? String(msg[this.child]) : ""; // get path from msg or default to / - childpath = (childpath.indexOf("/") == 0) ? childpath : "/" + childpath; // make sure the path starts with / + // Check credentials + if (!this.credentials.appid) { + firebaseStatus.error(this, 'Check credentials! (Query)'); + } else { + this.firebaseurl = 'https://' + this.credentials.appid + '.firebaseio.com/' + this.firebasepath; + this.firebase = new Firebase(this.firebaseurl); - this.firebase.child(childpath).once('value', sendMessageFromSnapshot.bind(this, msg)); - }); + // Status + firebaseStatus.checkStatus(this); + + // Add listener + this.on('input', function (msg) { + var childpath = (this.child) ? String(msg[this.child]) : ''; // get path from msg or default to + childpath = (childpath.indexOf('/') == 0) ? childpath : '/' + childpath; // make sure the path starts with + this.firebase.child(childpath).once('value', sendMessageFromSnapshot.bind(this, msg)); + }); + } } - RED.nodes.registerType("firebase query", FirebaseQuery); -} + + RED.nodes.registerType('firebase query', FirebaseQuery); +}; diff --git a/firebase_watch.html b/firebase_watch.html index ba80a0f..9057325 100644 --- a/firebase_watch.html +++ b/firebase_watch.html @@ -1,14 +1,13 @@ diff --git a/firebase_watch.js b/firebase_watch.js index 749b018..da2b4c6 100644 --- a/firebase_watch.js +++ b/firebase_watch.js @@ -1,6 +1,5 @@ module.exports = function(RED) { - "use strict"; - var Firebase = require("firebase"); + 'use strict'; function sendMessageFromSnapshot(snapshot) { var msg = {}; @@ -10,18 +9,35 @@ module.exports = function(RED) { } function FirebaseWatch(n) { + var Firebase = require('firebase'), + firebaseStatus = require('./utility/status'); + RED.nodes.createNode(this,n); - this.firebaseurl = n.firebaseurl; - this.firebase = new Firebase(this.firebaseurl); + this.credentials = RED.nodes.getNode(n.firebaselogin).credentials; this.onValue = sendMessageFromSnapshot.bind(this); + this.firebasepath = n.firebasepath; + + // Status + firebaseStatus.connecting(this); + + // Check credentials + if (!this.credentials.appid) { + firebaseStatus.error(this,'Check credentials!'); + this.error('You need to setup Firebase credentials!'); + } else { + this.firebaseurl = 'https://' + this.credentials.appid + '.firebaseio.com/' + this.firebasepath; + this.firebase = new Firebase(this.firebaseurl); - this.firebase.on("value", this.onValue); + // Status + firebaseStatus.checkStatus(this); - this.on('close', function() { - // We need to unbind our callback, or we'll get duplicate messages when we redeploy - this.firebase.off("value", this.onValue); - }); + this.firebase.on('value', this.onValue); + this.on('close', function() { + // We need to unbind our callback, or we'll get duplicate messages when we redeploy + this.firebase.off('value', this.onValue); + }); + } } - RED.nodes.registerType("firebase watch", FirebaseWatch); -} + RED.nodes.registerType('firebase watch', FirebaseWatch); +}; diff --git a/package.json b/package.json new file mode 100644 index 0000000..85fe35c --- /dev/null +++ b/package.json @@ -0,0 +1,44 @@ +{ + "name": "node-red-firebase", + "version": "0.1.2", + "description": "Check it out! Now you can access your Firebase data with Node-RED! This allows you to automate Firebase data manipulation or generate custom events based on what's going on with your data store.", + "main": "firebase_query.js", + "repository": { + "type": "git", + "url": "https://github.com/vergissberlin/node-red-firebase.git" + }, + "scripts": { + "test": "npm install" + }, + "keywords": [ + "node-red", + "node", + "firebase", + "real", + "time", + "socket" + ], + "node-red": { + "nodes": { + "firebase_modify": "firebase_modify.js", + "firebase_query": "firebase_query.js", + "firebase_watch": "firebase_watch.js" + } + }, + "author": "André Lademann ", + "contributors": [ + "André Lademann ", + "Hovis " + ], + "license": "MIT", + "bugs": { + "url": "https://github.com/vergissberlin/node-red-firebase/issues" + }, + "homepage": "https://github.com/vergissberlin/node-red-firebase", + "dependencies": { + "firebase": "^5.1.0", + "firebase-login": "^1.0.0", + "firebase-login-custom": "0.0.4", + "firebase-login-email": "^0.0.4" + } +} diff --git a/utility/status.js b/utility/status.js new file mode 100644 index 0000000..c276cbe --- /dev/null +++ b/utility/status.js @@ -0,0 +1,124 @@ +/** + * Status + * + * @type {{offline: Function, connecting: Function, connected: Function, error: Function, addListener: Function}} + */ +module.exports = { + + /** + * Status offline + * + * @param node + */ + offline: function (node) { + node.status({ + fill: 'gray', + shape: 'ring', + text: 'disconnected' + } + ); + }, + + /** + * Status connecting + * + * @param node + */ + connecting: function (node) { + node.status({ + fill: 'grey', + shape: 'ring', + text: 'connecting' + } + ); + }, + + /** + * Status connected + * + * @param node + */ + connected: function (node) { + node.status({ + fill: 'green', + shape: 'dot', + text: 'connected' + } + ); + }, + + /** + * Status error + * + * @param node + * @param msg + */ + error: function (node, msg) { + node.status({ + fill: 'red', + shape: 'ring', + text: msg || 'connection failed' + } + ); + }, + + /** + * AddEventListener + * + * @param node + */ + addListener: function (node) { + + var self = this; + + global.refFirebase.onAuth(function(authData) { + if (authData) { + self.connected(node); + } else { + self.error(node); + } + }); + + global.refFirebase.offAuth(function(authData) { + if (authData) { + self.connected(node); + } else { + self.offline(node); + } + }); + }, + + /** + * AddEventListener + * + * @param node + */ + checkStatus: function (node) { + + var authData, + self = this; + + node.firebase.onAuth(function(authData) { + if (authData) { + self.connected(node); + } else { + self.error(node); + } + }); + + node.firebase.offAuth(function(authData) { + if (authData) { + self.connected(node); + } else { + self.offline(node); + } + }); + + authData = node.firebase.getAuth(); + if (authData) { + self.connected(node); + } else { + self.offline(node); + } + } +};