Firebase Cloud function not updating data in Firestore database - javascript

So I'm writing a scheduled cloud function that is supposed to run every minute and update certain values in my Firestore database. I'm getting a success on the function and I don't see any errors, however, the database isn't updating. My project is made with flutter but I wrote the cloud function with node.js
enter image description here
This is my cloud function code.
const functions = require('firebase-functions');
const admin = require('firebase-admin');
admin.initializeApp(functions.config().firebase);
exports.myScheduledCloudFunction = functions.pubsub.schedule('* * * * *').timeZone('Asia/Kuala_Lumpur').onRun(async (context) => {
admin.firestore().collection('users').get().then(function(querySnapshot) {
querySnapshot.forEach(function(doc) {
admin.firestore().collection('users').doc(doc.id).collection('habits').get().then(function(querySnapshot) {
querySnapshot.forEach(function(habitDoc) {
admin.firestore().collection('users').doc(doc.uid).collection('habits').doc(habitDoc.id).ref.update({
'iscompleted' : false,
'completedcount': 0
})
})
})
})
})
return null;
});
I feel like it might be related to the rules but I'm not sure, for now my rules are default I haven't changed them. I hope someone can help

Thanks to #Doug Stevenson I've learned that I wasn't giving any time for the async functions to take its course. I have fixed my work and now it is working.
here's the fixed code:
exports.myScheduledCloudFunction = functions.pubsub.schedule('* * * * *').timeZone('Asia/Kuala_Lumpur').onRun(async (context) => {
const querySnapshot = await admin.firestore().collection('users').get();
querySnapshot.forEach(async (doc) => {
const habitQuerySnapshot = await admin.firestore().collection('users').doc(doc.id).collection('habits').get();
habitQuerySnapshot.forEach(async (habitDoc) => {
if (doc && doc.ref) {
await habitDoc.ref.update({
'iscompleted' : false,
'completedcount': 0
});
}
});
});
return null;
});

Related

How to check when firebase user was created?

Im using a schedule function to delete unverified users. The schedule function runs every 24 hours. Now the Problem that I have is : Lets say a user created his account 3 minutes ago and did not verify his email address yet. And then the functions runs and delete his/her/it Account. What I want is to give the user lets say 5 hours to verify his account . So my question is how can I check that in my function ?
Here's my function
import * as functions from "firebase-functions";
import admin from "firebase-admin";
export default functions.pubsub.schedule('every 24 hours').onRun(async(context) => {
console.log('This will be run every 24 hours!');
const users = []
const listAllUsers = (nextPageToken) => {
// List batch of users, 1000 at a time.
return admin.auth().listUsers(1000, nextPageToken).then((listUsersResult) => {
listUsersResult.users.forEach((userRecord) => {
users.push(userRecord)
});
if (listUsersResult.pageToken) {
// List next batch of users.
listAllUsers(listUsersResult.pageToken);
}
}).catch((error) => {
const functions = require("firebase-functions");
functions.logger.error("Hello from info. Here's an problem:", error);
});
};
// Start listing users from the beginning, 1000 at a time.
await listAllUsers();
const unVerifiedUsers = users.filter((user) => !user.emailVerified).map((user) => user.uid)
//DELETING USERS
return admin.auth().deleteUsers(unVerifiedUsers).then((deleteUsersResult) => {
console.log(`Successfully deleted ${deleteUsersResult.successCount} users`);
console.log(`Failed to delete ${deleteUsersResult.failureCount} users`);
deleteUsersResult.errors.forEach((err) => {
console.log(err.error.toJSON());
});
return true
}).catch((error) => {
const functions = require("firebase-functions");
functions.logger.error("Hello from info. Here's an problem:", error);
return false
});
});
If any question please let me know !
That's not possible with the Firebase Auth APIs. You will have to record the time on your own in a separate database, and using that database to find out the time you need to know.

Send a notification every 15 minutes during the day

I want to send a notification within 1 hour of the data I added to Firebase and I want to cancel the notification 1 day after adding the data. Because I don't know JavaScript, and I'm new to the software world yet, I couldn't quite figure out its algorithm, and I wrote something like that. The addedNewCard method works, but I couldn't adjust the time.
const functions = require('firebase-functions');
const admin = require('firebase-admin');
admin.initializeApp(functions.config().functions);
var newData;
exports.addedNewCard =
functions.firestore.document('Users/{userID}/Cards/{cardID}').onCreate(async
(snapshot, context) => {
const cardID = context.params.cardID;
if (snapshot.empty) {
console.log('No Devices');
return;
}
newData = snapshot.data();
const cardAddedDate = newData.cardAddedDate;
const deviceIdTokens = await admin
.firestore()
.collection('DeviceToken')
.get();
var tokens = [];
for (var token of deviceIdTokens.docs) {
tokens.push(token.data().deviceToken);
}
var payload = {
notification: {
title: 'Tekrar vakti',
body: 'Tekrar etmen gereken kelimeler var!!!',
sound: 'default',
},
data: {
click_action: 'FLUTTER_NOTIFICATIoN_CLICK',
sendCardID: cardID,
}
};
const options = {
priority: "high",
};
try {
await admin.messaging().sendToDevice(tokens, payload, options);
console.log('Notification sent successfully');
} catch (err) {
console.log(err);
}
})
exports.timeSettings = functions.pubsub.schedule('every 1 mins').onRun(async
(context) => {
console.log(context.timestamp);
let now = new Date();
const finishWorking = now.setDate(now.getDate + 1);
const finishWorkingStamp = admin.firestore.Timestamp.fromDate(finishWorking);
db.collection('Users/{userID}/Cards').where('cardAddedDate', '<',
finishWorkingStamp).get().then((snap) => {
if (snap.exist) {
snap.forEach(() => {
return addedNewCard();
}).catch((e) => {
return console.log(e);
});
}
});
})
Thanks to your comment, I would recommend you to use Cloud Task. With Cloud Task you can delay an execution in the futur.
When a user send a data, you can plan the 24H notification in advance (with 1 notification every 15 minutes, for 1 day -> create 96 tasks, the next one with 15 minutes more in the delay than the previous one).
You have code sample here. Have a look on this part, to change the delay
if (inSeconds) {
// The time when the task is scheduled to be attempted.
task.scheduleTime = {
seconds: inSeconds + Date.now() / 1000,
};
}
I wouldn't do the schedule notification in client side, instead, config and send schedule by server side. Try to create thread for client for processing the notifications.
You have to create a firebase cloud function where you need to upgrade your firebase account subscription and use pub-sub.
const functions = require('firebase-functions');
const admin = require('firebase-admin');
admin.initializeApp();
exports.scheduledFunctionCrontab = functions.pubsub.schedule('*/15 * * * *').timeZone('Asia/Kolkata').onRun(async (context) => {
console.log('This will be run every 15 minutes');
return null});

Set on firebase and then set firebase claims

So i working with firebase auth and database in order to set new user to data base, if set successful i want to set claims for that user.
So it means i have a promise within a promise:
function setUser(user){
// no need for the database code before this, but userRef is set properly
return userRef.set(user)
.then(succ => {
return firebase.firebase.auth().setCustomUserClaims(user.key, {admin: true})
.then(() => {
console.log("setting claims")
return true;
});
})
.catch(err => {
return err
})
}
calling function:
app.post("/register_user",jsonParser,async (req, res) => {
var user = req.body.user;
let result = await fireBase.setUser(user);
res.send(result);
})
What happens is that i get the set on the database but claims are not set nor i can i see the log. I know its a js question and not firebase one. I tried many different ways (with await) but non worked.
firebase.firebase does not seem correct. You need to be using the admin object which can be initialised using const admin = require('firebase-admin'); This is not part of the firebase db sdk, but the admin one. You can also use the userRef.uid as that gives you the id of the document of the user, if that is what you want, else use your user.key
return admin.auth().setCustomUserClaims(userRef.uid, {
admin: true
}).then(() => {
//on success
});

How do I call the function with a parameter I set up in firebase functions

I've deployed this code to my firebase functions project:
import * as functions from 'firebase-functions'
import * as admin from 'firebase-admin'
admin.initializeApp()
export const getEmail = functions.https.onRequest((request, response) => {
var from = request.body.sender;
admin.auth().getUserByEmail(from)
.then(snapshot => {
const data = snapshot.toJSON()
response.send(data)
})
.catch(error => {
//Handle error
console.log(error)
response.status(500).send(error)
})
})
Which takes in a email parameter that it gets from the user's input on my app. My app's code looks like this:
Functions.functions().httpsCallable("https://us-central1-projectname.cloudfunctions.net/getEmail").call(email) { (result, error) in
if let error = error as NSError? {
if error.domain == FunctionsErrorDomain {
//email isnt taken
let code = FunctionsErrorCode(rawValue: error.code)
let message = error.localizedDescription
let details = error.userInfo[FunctionsErrorDetailsKey]
print(code, message, details)
}
// ...
}
if let text = (result?.data as? [String: Any])?["text"] as? String {
// email taken
}
}
When I run the app and when that function is called, it seems to do nothing, no error message is shown and no data has been sent back. What am I missing?
Update: I went to the logs and nothing has happened in there as if the function was never called.
You are actually mixing up HTTP Cloud Functions and Callable Cloud Functions:
You Cloud Function code corresponds to an HTTP one but the code in your front-end seems to call a Callable one.
You should adapt one or the other, most probably adapt your Cloud Function to a Callable one, along the following lines:
exports.getEmail = functions.https.onCall((data, context) => {
const from = data.sender;
return admin.auth().getUserByEmail(from)
.then(userRecord => {
const userData = userRecord.toJSON();
return { userData: userData };
})
});
Have a look at the doc for more details, in particular how to handle errors. The doc is quite detailed and very clear.

How to make a subquery synchronously in Firebase realtime with Cloud Funtions

I'm using firebase functions with JavaScript to make a query about Firebase realtime, which will be used in an Android application. The main query obtains a collection of values that I then go through, and for each of them I launch another query. The results obtained from the second query stores in an array that is the one that I return as a result.
The problem is that the array that I return as an answer is empty, since the response is returned before the subqueries that add the data to the array end, that is, the problem I think is due to the asynchronous nature of the calls.
I have tried to reorganize the code in several ways and use promises to try that the result is not sent until all the queries have been made but the same problem is still happening.
The structure of the JSON database that I consult is the following:
"users" : {
"uidUser" : {
"friends" : {
"uidUserFriend" : "mail"
},
"name" : "nameUser",
...
},
"uidUser2" : {
"friends" : {
"uidUserFriend" : "mail"
},
"name" : "nameUser",
...
}
}
The functions are:
const functions = require('firebase-functions');
const admin = require('firebase-admin');
admin.initializeApp();
exports.searchFriends = functions.https.onCall((data,context) => {
const db = admin.database();
const uidUser = data.uidUser;
var arrayOfResults = new Array();
const refFriends = db.ref('/users/'+uidUser+'/friends');
let user;
return refFriends.once('value').then((snapshot) => {
snapshot.forEach(function(userSnapshot){
user = findUser(userSnapshot.key);
arrayOfResults.push(user);
});
return {
users: arrayOfResults
};
}).catch((error) => {
throw new functions.https.HttpsError('unknown', error.message, error);
});
});
function findUser(uid){
const db = admin.database();
const ref = db.ref('/users/'+uid);
return ref.once('value').then((snapshot) => {
console.log(snapshot.key,snapshot.val());
return snapshot.val();
}).catch((error) => {
console.log("Error in the query - "+error);
});
}
I do not know if the problem is because I do not manage the promises well or because I have to orient the code in another way.
Thank you.
Indeed, as you mentioned, you should "manage the promises" differently. Since you are triggering several asynchronous operations in parallel (with the once() method, which returns a promise) you have to use Promise.all().
The following code should do the trick:
const functions = require('firebase-functions');
const admin = require('firebase-admin');
admin.initializeApp();
exports.searchFriends = functions.https.onCall((data,context) => {
const db = admin.database();
const uidUser = data.uidUser;
var arrayOfPromises = new Array();
const refFriends = db.ref('/users/' + uidUser + '/friends');
let user;
return refFriends.once('value').then(snapshot => {
snapshot.forEach(userSnapshot => {
user = findUser(userSnapshot.key);
arrayOfPromises.push(user);
});
return Promise.all(arrayOfPromises);
})
.then(arrayOfResults => {
return {
users: arrayOfResults
};
})
.catch((error) => {
throw new functions.https.HttpsError('unknown', error.message, error);
});
});
function findUser(uid){
const db = admin.database();
const ref = db.ref('/users/' + uid);
return ref.once('value').then(snapshot => {
console.log(snapshot.key,snapshot.val());
return snapshot.val();
});
}
Note that I have modified the name of the first array to arrayOfPromises, which makes more sense IMHO.
Note also that you receive the results of Promise.all() in an array corresponding to the fulfillment values in the same order than the queries array, see: Promise.all: Order of resolved values

Categories

Resources