added on nodes for rtdb and firestore andmade nodes pass through msg objects with as little changes as possible

This commit is contained in:
Peter Svensson
2019-07-15 10:27:01 +02:00
parent e9fd502879
commit fefe2f2509
24 changed files with 453 additions and 48 deletions

View File

@ -29,14 +29,26 @@ Get data from a path in the rtdb database
input: {"payload": {"path": "foo/bar"}}
output: <whatever data was at the path "foo/bar" in the rtdb database>
output: whatever data was at the path "foo/bar" in the rtdb database
## rtdb-on
Set up a snapshot listsner for a path in the rtdb database
input: {"payload": {"path": "foo/bar"}}
output: whatever data was at the path "foo/bar" in the rtdb database, when changed
## rtdb-set
Set data at a path in the rtdb database. Use "on" snapshot so will fire every time the data at the path changes and so drive flow execution from that point.
input: {"payload": {"path": "foo/bar", "obj": {"the": "object"}}
## rtdb-push
Pushes the new object onto an array under the path
input: {"payload": {"path": "foo/bar", "obj": {"the": "object"}}
## rtdb-query
Set up a reactive query for a path in the rtdb database.
@ -63,7 +75,14 @@ Get data from a document path in the firestore database
input: {"payload": {"path": "foo/bar"}}
output: <the document at the path "foo/bar" in the firestore database>
output: the document at the path "foo/bar" in the firestore database
## firestore-on
Set up a snapshot listener fo a document path in the firestore database
input: {"payload": {"path": "foo/bar"}}
output: the document at the path "foo/bar" in the firestore database, when changed
## firestore-set
Set data at a path in the firestore database. Uses "onSnapshot" so will fire every time the data at the path changes and so drive flow execution from that point.
@ -115,7 +134,7 @@ input:
}
}
npm publish .output: Buffer object containing the binary file contents. Can easily be converted to a string by calling toString() on the Buffer.
output: Buffer object containing the binary file contents. Can easily be converted to a string by calling toString() on the Buffer.
## storage-write
Writes the content of JavaScript Buffer object to a file path in a storage bucket.

View File

@ -47,4 +47,10 @@
<script type="text/x-red" data-help-name="firestore-add">
<p>A node that wraps the firestore-add SDK</p>
Adds the new object under the collection the path describes and assigns it a random id
<p>
input: {"payload": {"path": "foo/bar"}, {"some": object, "foo": 17}}
<p>
output: The id of the new document
</script>

View File

@ -53,4 +53,9 @@
<script type="text/x-red" data-help-name="firestore-get">
<p>A node that wraps the firestore-get SDK</p>
Get data from a document path in the firestore database
<p>
input: {"payload": {"path": "foo/bar"}}
<p>
output: <the document at the path "foo/bar" in the firestore database>
</script>

View File

@ -1,5 +1,5 @@
let msgin
let unsub
module.exports = function(RED) {
@ -16,7 +16,7 @@ module.exports = function(RED) {
if(unsub){
unsub()
}
this.admin.firestore().doc(path).onSnapshot(cb)
this.admin.firestore().doc(path).get().then(cb)
}
const cb = (res)=>{
@ -24,11 +24,17 @@ module.exports = function(RED) {
console.dir(res)
let val = res.data()
console.log('val='+val)
if(msgin){
msgin.payload = val
node.send(msgin)
} else {
node.send({payload: val})
}
}
node.on('input', function(msg) {
if(msg && msg.payload){
msgin = msg
const path = msg.payload.path
setup(path)

View File

@ -0,0 +1,61 @@
<script type="text/javascript">
RED.nodes.registerType('firestore-on',{
category: 'firebase-admin',
color: '#a6bbcf',
defaults: {
name: {value:""},
path: {value:""},
cred: {value: "", type: 'firebase-config'}
},
inputs:1,
outputs:1,
icon: "firebase-admin-icon.png",
label: function() {
return this.name||"firestore-on";
},
oneditsave: function() {
let type = $('#apitype-select').val()
console.log('type is set to '+type)
},
oneditprepare: function() {
$('#apitype-select').change(function () {
$("#node-input-apitype").val($(this).find('option:selected').val())
});
$("#apitype-select").val($("#node-input-apitype").val())
$('#apitype-select').trigger('change');
}
});
</script>
<script type="text/x-red" data-template-name="firestore-on">
<div class="form-row">
<label for="node-input-name"><i class="icon-tag"></i> Name</label>
<input type="text" id="node-input-name" placeholder="Name">
</div>
<div class="form-row">
<label for="node-input-path"><i class="icon-tag"></i> Path</label>
<input type="text" id="node-input-path" placeholder="Path">
</div>
<div class="form-row">
<label for="node-input-cred"><i class="icon-tag"></i> Credentials</label>
<input type="text" id="node-input-cred" placeholder="Credentials">
</div>
</script>
<script type="text/x-red" data-help-name="firestore-on">
<p>A node that wraps the firestore-on SDK</p>
Get data from a document path in the firestore database
<p>
input: {"payload": {"path": "foo/bar"}}
<p>
output: <the document at the path "foo/bar" in the firestore database>
</script>

51
firestore/firestore-on.js Normal file
View File

@ -0,0 +1,51 @@
let msgin
let unsub
module.exports = function(RED) {
function FirebaseAdmin(config) {
RED.nodes.createNode(this, config);
var node = this;
if(config.cred){
let c = RED.nodes.getNode(config.cred)
this.admin = c.admin
}
const setup = ()=>{
if(unsub){
unsub()
}
this.admin.firestore().doc(path).onSnapshot(cb)
}
const cb = (res)=>{
console.log('firestore get result '+res)
console.dir(res)
let val = res.data()
console.log('val='+val)
if(msgin){
msgin.payload = val
node.send(msgin)
} else {
node.send({payload: val})
}
}
node.on('input', function(msg) {
if(msg && msg.payload){
msgin = msg
const path = msg.payload.path
setup(path)
}
}.bind(this));
if(config.path){
setup(config.path)
}
}
RED.nodes.registerType("firestore-on", FirebaseAdmin);
}

View File

@ -47,4 +47,25 @@
<script type="text/x-red" data-help-name="firestore-query">
<p>A node that wraps the firestore-query SDK</p>
Set up a reactive query for a collection in the firestore database.
<p>
input:
<p>
{
"payload": {
"path": "foo/bar",
"queries":[],
"limit": 7,
"startAt": 4000, // follows the orderBy property
"endAt": 4050, // follows the orderBy property
"orderBy": "shoeSize",
"orderDirection": "asc", //default is "desc"
"queries":[
{"company", "==", "ACME"},
{"createdAt", ">", 1560099394242}
]
}
<p>
output: An array of the results of the query.
</script>

View File

@ -1,4 +1,4 @@
let msgin
let unsub
module.exports = function(RED) {
@ -16,13 +16,15 @@ module.exports = function(RED) {
console.dir(res)
let val = res.docs.map((d)=>{return d.data()})
console.log('val='+val)
node.send({payload:val})
msgin.payload = val
node.send(msgin)
}
node.on('input', function(msg) {
console.log('firestore-query got input')
console.dir(msg.payload)
if(msg && msg.payload){
msgin = msg
const path = msg.payload.path
if(unsub){
unsub()

View File

@ -47,4 +47,7 @@
<script type="text/x-red" data-help-name="firestore-set">
<p>A node that wraps the firestore-set SDK</p>
Set data at a path in the firestore database. Uses "onSnapshot" so will fire every time the data at the path changes and so drive flow execution from that point.
<p>
input: {"payload": {"path": "foo/bar"}, {"some": object, "foo": 17}}
</script>

View File

@ -1,6 +1,6 @@
{
"name": "node-red-contrib-firebase-admin",
"version": "1.1.3",
"version": "1.1.4",
"description": "A node-red module that wraps the server-side admin SDK of firebase, firestore, et.c.",
"main": "index.js",
"scripts": {
@ -13,7 +13,10 @@
"author": "",
"license": "MIT",
"keywords": [
"node-red"
"node-red",
"firebase",
"firestore",
"cloud storage"
],
"bugs": {
"url": "https://github.com/psvensson/node-red-contrib-firebase-admin/issues"
@ -24,10 +27,12 @@
"rtdb-set": "rtdb/rtdb-set.js",
"rtdb-push": "rtdb/rtdb-push.js",
"rtdb-get": "rtdb/rtdb-get.js",
"rtdb-on": "rtdb/rtdb-on.js",
"rtdb-query": "rtdb/rtdb-query.js",
"firestore-set": "firestore/firestore-set.js",
"firestore-add": "firestore/firestore-add.js",
"firestore-get": "firestore/firestore-get.js",
"firestore-on": "firestore/firestore-on.js",
"firestore-query": "firestore/firestore-query.js",
"firebase-config": "firebase-config.js",
"flow-to-rtdb": "rtdb/flow-to-rtdb.js",

View File

@ -52,5 +52,10 @@
</script>
<script type="text/x-red" data-help-name="rtdb-get">
<p>A node that wraps the rtdb-get SDK</p>
<p>Get data from a path in the rtdb database </p>
<p>
input: {"payload": {"path": "foo/bar"}}
</p><p>
output: whatever data was at the path "foo/bar" in the rtdb database</p>
</p>
</script>

View File

@ -1,6 +1,5 @@
let oldpath
let msgin
module.exports = function(RED) {
@ -9,20 +8,31 @@ module.exports = function(RED) {
var node = this;
const cb = (res)=>{
console.log('firebase get result '+res)
console.log('firebase rtdb-get result '+res)
//console.dir(res)
let val = res.val()
console.dir(val)
//console.dir(val)
if(msgin){
msgin.payload = val
node.send(msgin)
} else {
node.send({payload: val})
}
}
let setUpListener = (path)=>{
console.log('rtdb-get setUpListener for path '+path)
if(oldpath){
this.admin.database().ref(oldpath).off('value', cb)
console.log('* rtdb-get setUpListener for path '+path)
if(path){
this.admin.database().ref(path).once('value').then((res)=>{
cb(res)
}, (fail)=>{
console.log('rtdb-get failure ')
console.dir(fail)
})
} else {
console.log('----- rtdb-get got empty path !!')
console.dir(config)
}
this.admin.database().ref(path).on('value', cb)
oldpath = path
}
if(config.cred){
@ -43,11 +53,11 @@ module.exports = function(RED) {
let path = this.path
if(msg && msg.payload){
path = msg.payload.path
msgin = msg
setUpListener(path)
}
}.bind(this));
setUpListener(this.path)
}
RED.nodes.registerType("rtdb-get", FirebaseAdmin);

61
rtdb/rtdb-on.html Normal file
View File

@ -0,0 +1,61 @@
<script type="text/javascript">
RED.nodes.registerType('rtdb-on',{
category: 'firebase-admin',
color: '#a6bbcf',
defaults: {
name: {value:""},
path: {value:""},
cred: {value: "", type: 'firebase-config'}
},
inputs:1,
outputs:1,
icon: "firebase-admin-icon.png",
label: function() {
return this.name||"rtdb-on";
},
oneditsave: function() {
let type = $('#apitype-select').val()
console.log('type is set to '+type)
},
oneditprepare: function() {
$('#apitype-select').change(function () {
$("#node-input-apitype").val($(this).find('option:selected').val())
});
$("#apitype-select").val($("#node-input-apitype").val())
$('#apitype-select').trigger('change');
}
});
</script>
<script type="text/x-red" data-template-name="rtdb-on">
<div class="form-row">
<label for="node-input-name"><i class="icon-tag"></i> Name</label>
<input type="text" id="node-input-name" placeholder="Name">
</div>
<div class="form-row">
<label for="node-input-path"><i class="icon-tag"></i> Path</label>
<input type="text" id="node-input-path" placeholder="Path">
</div>
<div class="form-row">
<label for="node-input-cred"><i class="icon-tag"></i> Credentials</label>
<input type="text" id="node-input-cred" placeholder="Name">
</div>
</script>
<script type="text/x-red" data-help-name="rtdb-on">
<p>Get data from a path in the rtdb database </p>
<p>
input: {"payload": {"path": "foo/bar"}}
</p><p>
output: whatever data was at the path "foo/bar" in the rtdb database</p>
</p>
</script>

65
rtdb/rtdb-on.js Normal file
View File

@ -0,0 +1,65 @@
let oldpath
let msgin
module.exports = function(RED) {
function FirebaseAdmin(config) {
RED.nodes.createNode(this, config);
var node = this;
const cb = (res)=>{
console.log('firebase get result '+res)
//console.dir(res)
let val = res.val()
//console.dir(val)
if(msgin){
msgin.payload = val
node.send(msgin)
} else {
node.send({payload: val})
}
}
let setUpListener = (path)=>{
console.log('rtdb-on setUpListener for path '+path)
if(oldpath){
this.admin.database().ref(oldpath).off('value', cb)
}
if(path){
this.admin.database().ref(path).on('value', cb)
oldpath = path
} else {
console.log('----- rtdb-on got empty path !!')
console.dir(config)
}
}
if(config.cred){
let c = RED.nodes.getNode(config.cred)
this.admin = c.admin
}
//console.log('------------------------------- rtdg-get config')
//console.dir(config)
this.path = config.path
if(this.path){
setUpListener(this.path)
}
//console.log('configuring rtdb-on to listen for messages')
node.on('input', function(msg) {
let path = this.path
if(msg && msg.payload){
path = msg.payload.path
msgin = msg
setUpListener(path)
}
}.bind(this));
}
RED.nodes.registerType("rtdb-on", FirebaseAdmin);
}

View File

@ -47,4 +47,7 @@
<script type="text/x-red" data-help-name="rtdb-push">
<p>A node that wraps the rtdb-push SDK.</p>
Pushes the new object onto an array under the path
<p>
input: {"payload": {"path": "foo/bar", "obj": {"the": "object"}}
</script>

View File

@ -47,4 +47,21 @@
<script type="text/x-red" data-help-name="rtdb-query">
<p>A node that wraps the rtdb-query SDK</p>
Set up a reactive query for a path in the rtdb database.
<p>
input: {"payload": {"path": "foo/bar", queries:[], on: "value}}
<p>
on: "value" (can also be "child_added", "child_removed", "child_changed", "child_moved").
If an "on" property is missing, on: "value" is assumed as default
<p>
Where each query is an object that can look like either of the following examples;
<p>
- {"startAt": "foo"}
- {"endAt": "bar"}
- {"equalTo": "quux"}
- {"orderBy": "child", "value": "height"} (can also be "key" or "value)
- {"limitTo": "last", "value": 3} (can also be "first")
<p>
output: [an array of results for the query]
</script>

View File

@ -1,5 +1,5 @@
let msgin
let oldpath
let oldeventtype
@ -18,11 +18,13 @@ module.exports = function(RED) {
console.dir(res)
let val = res.val()
console.log('val='+val)
node.send({payload:val})
msgin.payload = val
node.send(msgin)
}
node.on('input', function(msg) {
if(msg && msg.payload){
msgin = msg
const path = msg.payload.path
const eventtype = msg.payload.on || 'value'
if(oldpath){

View File

@ -47,4 +47,7 @@
<script type="text/x-red" data-help-name="rtdb-set">
<p>A node that wraps the rtdb-set SDK</p>
Set data at a path in the rtdb database. Use "on" snapshot so will fire every time the data at the path changes and so drive flow execution from that point.
<p>
input: {"payload": {"path": "foo/bar", "obj": {"the": "object"}}
</script>

View File

@ -46,4 +46,25 @@
<script type="text/x-red" data-help-name="storage-list">
<p>A node that list files from bucket path in google cloud storage</p>
Lists the contents of files in a bucket
<p>
If "path" is defined in the payload, only files beginning with that path will be returned. I fomitted, the root level of the bucket is listed.
A path can also be deep likes this; "foo/bar/baz". "delimiter" is the charatcer used to delimit directory levels, "/" by default.
<p>
input:
{
"payload": {
"bucket": "xyzzyz123.appspot.com", // optional
"path": "directory1", // optional
"delimiter": "/" // optional
}
}
<p>
output: An array of google cloud-storage File objects. If you take this output and send it to a function which outputs a payload like this;
{"files": array_of_File_obejcts}
<p>
The storage-read module will read all file contents and output an object of filename keyed Buffer objects instead of the normal one.
</script>

View File

@ -1,7 +1,5 @@
let oldpath
module.exports = function(RED) {
function FirebaseAdmin(config) {
@ -37,7 +35,8 @@ module.exports = function(RED) {
console.log('got file listing')
//console.dir(files[0])
let f = files[0].filter((e)=>{ return e.name[e.name.length-1] !== '/' })
node.send({payload: {files: f}})
msg.payload = {files: f}
node.send(msg)
})
}
}.bind(this));

View File

@ -40,4 +40,19 @@
<script type="text/x-red" data-help-name="storage-read">
<p>A node that read files from bucket path in google cloud storage</p>
Read file data from a file at a given path under a given cloud storage bucket. The default bucket to be used can be set in the general firebase SDK settings.
If the payload defines an optional bucket property, it will override the default bucket settings.
<p>
input:
{
"payload": {
"bucket": "xyzzyz123.appspot.com",
"path": "myFile.txt"
}
}
<p>
output: Buffer object containing the binary file contents. Can easily be converted to a string by calling toString() on the Buffer.
</script>

View File

@ -1,7 +1,5 @@
let oldpath
module.exports = function(RED) {
function FirebaseAdmin(config) {
@ -20,8 +18,8 @@ module.exports = function(RED) {
//console.log('configuring storage-read to listen for messages')
node.on('input', function(msg) {
if(msg && msg.payload){
let path = msg.payload.path || this.path
let bucket = msg.payload.bucket || this.bucket
let path = msg.payload.path || msg.path || this.path
let bucket = msg.payload.bucket || msg.bucket || this.bucket
console.log('------------------------------ storage-read reading from bucket "'+bucket+'" path "'+path+'"')
if(msg.payload.files && msg.payload.files.length > 0){
console.log('--reading from files')
@ -36,12 +34,13 @@ module.exports = function(RED) {
console.log('storage-read got file '+file.name)
rv[file.name] = content
if(--count === 0){
console.log('all files done, sending..')
for(let x in rv){
console.log(x)
}
node.send({payload:rv})
msg.payload = rv
node.send(msg)
}
}, (fail)=>{
console.log('storage-read could not find file '+path)
msg.payload = undefined
node.send(msg)
})
})(_file)
})
@ -52,8 +51,12 @@ module.exports = function(RED) {
.file(path).download().then((file)=>{
console.log('storage-read got file')
//console.dir(file)
node.send({payload:file})
msg.payload = file
node.send(msg)
}, (fail)=>{
console.log('storage-read could not find single file '+path)
msg.payload = undefined
node.send(msg)
})
}
}

View File

@ -40,4 +40,26 @@
<script type="text/x-red" data-help-name="storage-write">
<p>A node that read files from bucket path in google cloud storage</p>
Writes the content of JavaScript Buffer object to a file path in a storage bucket.
<p>
input:
{
"payload": {
"bucket": "abc.appspot.com", // optional, is otherwise set as node config
"path": "foo/bar/baz.json", // optional, see above
"contents": <Buffer obj>,
"contentType": "application/json" }, // optional
"metadata": { "very":"interesting"}, // optional
"public": true, // optional
"private": false // optional
}
}
<p>
output:
{
"payload": {"success": true, "filename": "foo/bar.txt"} // ot false if an error occurred
}
</script>

View File

@ -17,27 +17,27 @@ module.exports = function(RED) {
//console.log('configuring storage-write to listen for messages')
node.on('input', function(msg) {
if(msg && msg.payload){
let path = msg.payload.path || this.path
let bucket = msg.payload.bucket || this.bucket
let contents = msg.payload.contents
let path = msg.payload.path || msg.path|| this.path
let bucket = msg.payload.bucket || msg.bucket || this.bucket
let contents = msg.payload.contents || msg.payload
let options ={
contentType: msg.payload.contentType || 'auto',
metadata: msg.payload.metadata || {},
private: msg.payload.private || true,
public: msg.payload.public || true,
contentType: msg.payload.contentType || msg.contentType || 'auto',
metadata: msg.payload.metadata || msg.metadata || {},
private: msg.payload.private || msg.private || true,
public: msg.payload.public || msg.public || true,
}
console.log('storage-write writing file to bucket "'+bucket+'" path "'+path+'" contents is of type '+(typeof contents))
console.dir(contents)
const myBucket = this.storage.bucket(bucket);
const file = myBucket.file(path);
file.save(contents, options, function(err) {
if (!err) {
// File written successfully.
node.send({payload:{success:true, filename: path}})
msg.payload={success:true, filename: path}
} else {
console.log('cloud storage write error: '+err)
node.send({payload:{success:false, filename: path}})
msg.payload ={success:false, filename: path}
}
node.send(msg)
});
}
}.bind(this));