Firebase realtime database backend async function not updating users - javascript

I call this function when I have a list of users who i want to update. The console.logs and resolve message are in perfect order and what I expect. But nothing gets written/changed on the realtime database. It looks completely untouched. Did I do the promise wrong? Any guidance appreciated, thank you.
function updateUsers(){
return new Promise(resolve => {
objectLength = Object.keys(usersUpdate).length;
for (let i=0; i < objectLength; i++){
admin.database().ref('users/' + usersUpdate[i].key + '/notifications/' + usersUpdate[i].notif).update({
"sendPush" : 0,
})
.then(()=>{
console.log('updated user ' + i);
if (i == (objectLength-1)){
resolve('resolved updateUsers');
}
})
.catch((error) => console.log('Error ' + error));
}
})
}

Apparently firebase doesn't like being passed integers. Lesson learned. Turned the value to "0" instead of 0 and it worked.

Related

Is there a way to send a piece of data through a fetch() command, so it's returned with the response?

I'm running fetch() in a for loop, looping through an array of strings, and looking to handle each fetch response using a unique value (the for loop variable count).
Is there a way to pass data through a fetch command that's unrelated to the target API and get it back in the response?
Here is the code I have so far. The corresponding HTML has a series of images (pic1, pic2, etc.). I currently have "pic1" hard-coded in the .then(data) response handler, but would like to make that variable.
for (var n = 0; n < arrayLength; n++) {
console.log('Just inside arrayLength for loop');
console.log('Search term '+ n +' = ' + lines[n]);
//Do something
fetch("https://ws.audioscrobbler.com/2.0?method=album.search&album="+ lines[n] + "&api_key=" + lastfm_apikey + "&format=json")
.then((response) => {
// handle the response
return response.json();
})
.then((data) => {
let tunes = data;
console.log('n: ' + n);
let albumArtURL = data.results.albummatches.album[0].image[3]['#text'];
console.log('albumArtURL: ' + albumArtURL);
document.getElementById("pic1").src = albumArtURL;
})
.catch(function(error) {
// handle the error
console.log('Error returned: ' + error);
});
}
I have reviewed the Last.FM API and there doesn't look like there's a way to just pass a string through from the request back through the response.
I think you are over thinking it. You have the data so reference it in your then function.
for (let n = 0; n < arrayLength; n++) {
const line = lines[n];
fetch(`https://ws.audioscrobbler.com/2.0?method=album.search&album=${line}&api_key=${lastfm_apikey}&format=json`)
.then((response) => response.json())
.then((data) => {
console.log(n, line);
})
.catch((error) => {
console.error('Error returned: ' + error);
});
}

function call with ASYNC/AWAIT

I'm sure this issue is from my lack of async/await knowledge. However, I cannot figure out what I am doing wrong. Endgoal is for the addSupplier() process to wait on getSuppliers() function to finish processing until it continues.
Background info:
This is inside of a much larger Electron application utilizing HTML, JS, and Postgres. This snippet of code is processing a request from HTML form to insert a supplier into the table, then refresh the HTML table which is located on the same page. HTML code is not shown but I can include if needed.
Current code - not split into separate functions - works as desired:
function addSupplier(){
const connString = getConnData();
const client = new Pool(connString)
client.connectionTimeoutMillis = 4000;
var supplierID = document.getElementById("dbSupplierID").value;
var supplierName = document.getElementById("dbSupplierName").value;
var supplierUnit = document.getElementById("dbSupplierUnit").value;
const supplier_insert_query = {
text: "INSERT INTO suppliers(supplier_id, company, unit)VALUES('"+ supplierID +"', '"+ supplierName +"', '"+ supplierUnit +"')",
}
const supplier_select_query = {
text: "SELECT supplier_id AS id, company, unit FROM suppliers",
rowMode: 'array',
}
// Connect to client, catch any error.
client.connect()
.then(() => console.log("Connected Successfuly"))
.catch(e => console.log("Error Connecting"))
.then(() => client.query(supplier_insert_query))
.catch((error) => alert(error))
.then( () => { // After inserting new author, run author query and refresh table.
client.query(supplier_select_query)
.then(results => {
// Create the static header for the table
var table = ""; table_head = ""
for (i=0; i<results.fields.length; i++) { if (i==3) {continue}
table_head += '<th>' + results.fields[i].name + '</th>'
}
// Create the body of the table
for (j=0; j<results.rows.length; j++) { //j: rows, k: columns
results.rows[-1] = []
if (results.rows[j][0] == results.rows[j-1][0]) {continue}
table += '<tr>'
for (k=0; k<results.fields.length; k++) { if (k==3) {continue} // Skip creating the last column of this query. This data is only used to add information to the first column.
if (k==0) { // When you've reached a session_id cell, write the names of the tests in this session in that cell
var x=0; x = j; var tests = ''
while (results.rows.length > x && results.rows[x][0] == results.rows[j][0]) {
tests += '<br>' + (results.rows[x][3]==null ? '':results.rows[x][3]); x++
}
}
table += `<td>${results.rows[j][k]}` + (k==0 ? `${tests}`:``) + '</td>'
}
table += '</tr>'
}
// Grab the constructed HTML strings and write them to the document to create the table there
document.getElementById('supplier_table_head').innerHTML = ""
document.getElementById('supplier_table').innerHTML = ""
document.getElementById('supplier_table_head').innerHTML += table_head
document.getElementById('supplier_table').innerHTML += table
}).catch(e => console.error("ERROR in supplier table query\n",e.stack))
})
// Clearing input fields.
document.getElementById('dbSupplierID').value = ""
document.getElementById('dbSupplierName').value = ""
document.getElementById('dbSupplierUnit').value = ""
// Preventing app refresh
event.preventDefault()
.finally(() => client.end())
}
EDIT: I made the async changes and it seems to call the function correctly but the function does not process. This is what I have now. If I throw in an alert, it processes the alert 'TypeError: Promise resolver undefined is not a function' and then kicks out and refreshes the entire application.
// Function to add a supplier to the DB
async function addSupplier(){
const connString = getConnData();
const client = new Pool(connString)
client.connectionTimeoutMillis = 4000;
var supplierID = document.getElementById("dbSupplierID").value;
var supplierName = document.getElementById("dbSupplierName").value;
var supplierUnit = document.getElementById("dbSupplierUnit").value;
const supplier_insert_query = {
text: "INSERT INTO suppliers(supplier_id, company, unit)VALUES('"+ supplierID +"', '"+ supplierName +"', '"+ supplierUnit +"')",
}
// Connect to client, catch any error.
client.connect()
.then(() => console.log("Connected Successfuly")) // Connection is good
.catch(e => console.log("Error Connecting")) // CATCH - Connect error
.then(() => client.query(supplier_insert_query)) // Run supplier insertion query
.catch((error) => alert(error)) // CATCH - error in insertion query
.then(async () => {
// wait for getSuppliers function to execute
await getSuppliers();
})
.then(() => {
// Clearing input fields.
document.getElementById('dbSupplierID').value = ""
document.getElementById('dbSupplierName').value = ""
document.getElementById('dbSupplierUnit').value = ""
// Preventing app refresh
event.preventDefault()
})
.catch(e => console.error("ERROR in supplier table query\n",e.stack)) //Catch any error from getSuppliers
.finally(() => client.end()) // Close the connection
}
async function getSuppliers(){
let promise = new Promise()
// Query to pull all Suppliers info from database
const supplier_select_query = {
text: "SELECT supplier_id AS id, company, unit FROM suppliers",
rowMode: 'array',
}
// Query the database and pull the results into the HTML table
client.query(supplier_select_query)
.then(results => {
// Create the static header for the table
var table = ""; table_head = ""
for (i=0; i<results.fields.length; i++) { if (i==3) {continue}
table_head += '<th>' + results.fields[i].name + '</th>'
}
// Create the body of the table
for (j=0; j<results.rows.length; j++) { //j: rows, k: columns
results.rows[-1] = []
if (results.rows[j][0] == results.rows[j-1][0]) {continue}
table += '<tr>'
for (k=0; k<results.fields.length; k++) { if (k==3) {continue} // Skip creating the last column of this query. This data is only used to add information to the first column.
if (k==0) { // When you've reached a session_id cell, write the names of the tests in this session in that cell
var x=0; x = j; var tests = ''
while (results.rows.length > x && results.rows[x][0] == results.rows[j][0]) {
tests += '<br>' + (results.rows[x][3]==null ? '':results.rows[x][3]); x++
}
}
table += `<td>${results.rows[j][k]}` + (k==0 ? `${tests}`:``) + '</td>'
}
table += '</tr>'
}
// Grab the constructed HTML strings and write them to the document to create the table there
document.getElementById('supplier_table_head').innerHTML = ""
document.getElementById('supplier_table').innerHTML = ""
document.getElementById('supplier_table_head').innerHTML += table_head
document.getElementById('supplier_table').innerHTML += table
})
.finally(() => {return promise.resolve()})
}
The await keyword waits for a promise to be resolved before running code after it (await in Javascript). You haven't made getSuppliers create or resolve a promise, so await isn't waiting for anything here. I'm not very familiar with creating and resolving promises myself, but something like promise = new Promise() at the beginning of getSuppliers and return promise.resolve() at the end should work properly.
EDIT: It also seems like you're missing an async before the arrow function with await getSuppliers(), that could be causing issues as well. I might have jumped the gun on assuming promises were the issue, as the await keyword does try to convert anything that isn't a promise to a resolved one, but I'm not familiar with how consistently this works the way it should.
You need to prefix function declaration with async key word to use await key word inside of it.
client.connect()
.then(() => console.log("Connected Successfuly")) // Connection is good
.catch(e => console.log("Error Connecting")) // CATCH - Connect error
.then(() => client.query(supplier_insert_query)) // Run supplier insertion query
.catch((error) => alert(error)) // CATCH - error in insertion query
.then(async () => {
// wait for getSuppliers function to execute
await getSuppliers()
// Clearing input fields.
document.getElementById('dbSupplierID').value = ""
document.getElementById('dbSupplierName').value = ""
document.getElementById('dbSupplierUnit').value = ""
// Preventing app refresh
event.preventDefault()
})

Angularfire Increment transaction

Im having trouble incrementing a count of post "likes". The following is what I have right now:
addLike(pid, uid) {
const data = {
uid: uid,
};
this.afs.doc('posts/' + pid + '/likes/' + uid).set(data)
.then(() => console.log('post ', pid, ' liked by user ', uid));
const totalLikes = {
count : 0
};
const likeRef = this.afs.collection('posts').doc(pid);
.query.ref.transaction((count => {
if (count === null) {
return count = 1;
} else {
return count + 1;
}
}))
}
this obviously throws and error.
My goal is to "like" a post and increment a "counter" in another location. Possibly as a field of each Pid?
What am I missing here? I'm certain my path is correct..
Thanks in advance
You're to use the Firebase Realtime Database API for transactions on Cloud Firestore. While both databases are part of Firebase, they are completely different, and you cannot use the API from one on the other.
To learn more about how to run transactions on Cloud Firestore, see updating data with transactions in the documentation.
It'll look something like this:
return db.runTransaction(function(transaction) {
// This code may get re-run multiple times if there are conflicts.
return transaction.get(likeRef).then(function(likeDoc) {
if (!likeDoc.exists) {
throw "Document does not exist!";
}
var newCount = (likeDoc.data().count || 0) + 1;
transaction.update(likeDoc, { count: newCount });
});
}).then(function() {
console.log("Transaction successfully committed!");
}).catch(function(error) {
console.log("Transaction failed: ", error);
});

react native as soon as is array.push done

Is there any way how to catch when is array.push function done? I get data from Firebase database then I push data to array (which is state). Unfortunately as I noticed it changes my state once per every item.
Check screen for better understanding
I have 2 objects in database.
there is code:
componentWillMount(){
const self = this;
const rootRef = firebase.database().ref('/users/' + firebase.auth().currentUser.uid);
const ref = rootRef.child("MyTeams");
let teamsArr = [];
ref.once("value").then(function(snapshot) {
snapshot.forEach(function(childSnapshot) {
self.setState({TeamName: childSnapshot.val()});
teamsArr.push(self.state.TeamName);
self.setState({TeamsToMap: teamsArr});
self.setState({Loaded: true});
console.log('Here: ' + self.state.TeamsToMap);
})
})
.catch((error) =>{
alert(error.message);
})
};
WriteTeams(navigation){
if(this.state.TeamsToMap.length > 0 && this.state.Loaded === true){
console.log('Somewhere: ' + this.state.TeamsToMap.length);
}
}
Since you're looping over the snapshot, the array is modified many times. If you only want to print it after the modification are done: move the console.log() statement after the loop.
ref.once("value").then(function(snapshot) {
snapshot.forEach(function(childSnapshot) {
self.setState({TeamName: childSnapshot.val()});
teamsArr.push(self.state.TeamName);
self.setState({TeamsToMap: teamsArr});
self.setState({Loaded: true});
})
console.log('Here: ' + self.state.TeamsToMap);
})
Since both snapshot.forEach() and teamsArr.push() are synchronous here, the console.log() will run after teamsArr has been fully populated.

Firebase function execution and subscription to list that is being updated by a firebase function

I think a firebase function updating a list that I have in the firebase database is being captured by a subscription that is subscribed to that list. From what the list output looks like on my phone (in the app)...and from what my console output looks like (the way it repeats) it seems like it is capturing the whole list and displaying it each time one is added. So (I looked this up)...I believe this equation represents what is happening:
(N(N + 1))/2
It is how you get the sum of all of the numbers from 1 to N. Doing the math in my case (N = 30 or so), I get around 465 entries...so you can see it is loading a ton, when I only want it to load the first 10.
To show what is happening with the output here is a pastebin https://pastebin.com/B7yitqvD.
In the output pay attention to the array that is above/before length - 1 load. You can see that it is rapidly returning an array with one more entry every time and adding it to the list. I did an extremely rough count of how many items are in my list too, and I got 440...so that roughly matches the 465 number.
The chain of events starts in a page that isn't the page with the list with this function - which initiates the sorting on the firebase functions side:
let a = this.http.get('https://us-central1-mane-4152c.cloudfunctions.net/sortDistance?text='+resp.coords.latitude+':'+resp.coords.longitude+':'+this.username);
this.subscription6 = a.subscribe(res => {
console.log(res + "response from firesbase functions");
loading.dismiss();
}, err => {
console.log(JSON.stringify(err))
loading.dismiss();
})
Here is the function on the page with the list that I think is capturing the entire sort for some reason. The subscription is being repeated as the firebase function sorts, I believe.
loadDistances() {
//return new Promise((resolve, reject) => {
let cacheKey = "distances"
let arr = [];
let mapped;
console.log("IN LOADDISTANCES #$$$$$$$$$$$$$$$$$$$$$");
console.log("IN geo get position #$$$$$$$5354554354$$$$$$$");
this.distancelist = this.af.list('distances/' + this.username, { query: {
orderByChild: 'distance',
limitToFirst: 10
}});
this.subscription6 = this.distancelist.subscribe(items => {
let x = 0;
console.log(JSON.stringify(items) + " length - 1 load");
items.forEach(item => {
let storageRef = firebase.storage().ref().child('/settings/' + item.username + '/profilepicture.png');
storageRef.getDownloadURL().then(url => {
console.log(url + "in download url !!!!!!!!!!!!!!!!!!!!!!!!");
item.picURL = url;
}).catch((e) => {
console.log("in caught url !!!!!!!$$$$$$$!!");
item.picURL = 'assets/blankprof.png';
});
this.distances.push(item);
if(x == items.length - 1) {
this.startAtKey4 = items[x].distance;
}
x++;
})
//this.subscription6.unsubscribe();
})
}
The subscription in loadDistances function works fine as long as I don't update the list from the other page - another indicator that it might be capturing the whole sort and listing it repeatedly as it sorts.
I have tried as as I could think of to unsubscribe from the list after I update...so then I could just load the list of 10 the next time the page with the list enters, instead of right after the update (over and over again). I know that firebase functions is in beta. Could this be a bug on their side? Here is my firebase functions code:
exports.sortDistance = functions.https.onRequest((req, res) => {
// Grab the text parameter.
var array = req.query.text.split(':');
// Push the new message into the Realtime Database using the Firebase Admin SDK.
// Get a database reference to our posts
var db = admin.database();
var ref = db.ref("profiles/stylists");
var promises = [];
// Attach an asynchronous callback to read the data at our posts reference
ref.on("value", function(snapshot) {
//console.log(snapshot.val());
var snap = snapshot.val();
for(const user in snap) {
promises.push(new Promise(function(resolve, reject) {
var snapadd = snap[user].address;
console.log(snapadd + " snap user address (((((((())))))))");
if(snapadd != null || typeof snapadd != undefined) {
googleMapsClient.geocode({
address: snapadd
}).asPromise()
.then(response => {
console.log(response.json.results[0].geometry.location.lat);
console.log(" +++ " + response.json.results[0].geometry.location.lat + ' ' + response.json.results[0].geometry.location.lng + ' ' + array[0] + ' ' + array[1]);
var distanceBetween = distance(response.json.results[0].geometry.location.lat, response.json.results[0].geometry.location.lng, array[0], array[1]);
console.log(distanceBetween + " distance between spots");
var refList = db.ref("distances/"+array[2]);
console.log(snap[user].username + " snap username");
refList.push({
username: snap[user].username,
distance: Math.round(distanceBetween * 100) / 100
})
resolve();
})
.catch(err => { console.log(err); resolve();})
}
else {
resolve();
}
}).catch(err => console.log('error from catch ' + err)));
//console.log(typeof user + 'type of');
}
var p = Promise.all(promises);
console.log(JSON.stringify(p) + " promises logged");
res.status(200).end();
}, function (errorObject) {
console.log("The read failed: " + errorObject.code);
});
});
What is weird is, when I check the firebase functions logs, all of this appears to only run once...but I still think the subscription could be capturing the whole sorting process in some weird way while rapidly returning it. To be as clear as possible with what I think is going on - I think each stage of the sort is being captured in an (N(N + 1))/2...starting at 1 and going to roughly 30...and the sum of the sorting ends up being the length of my list (with 1-10 items repeated over and over again).
I updated to angularfire2 5.0 and angular 5.0...which took a little while, but ended up solving the problem:
this.distanceList = this.af.list('/distances/' + this.username,
ref => ref.orderByChild("distance").limitToFirst(50)).valueChanges();
In my HTML I used an async pipe, which solved the sorting problem:
...
<ion-item *ngFor="let z of (distanceList|async)" no-padding>
...

Categories